Skip to content

Commit

Permalink
add srcJsCode & onlyInternalCCIPContract
Browse files Browse the repository at this point in the history
  • Loading branch information
olegkron committed Apr 10, 2024
1 parent 6d62979 commit 0b7a435
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 10 deletions.
43 changes: 36 additions & 7 deletions packages/hardhat/contracts/CFunctions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ contract CFunctions is FunctionsClient, ConfirmedOwner, IFunctions {
mapping(bytes32 => Transaction) public transactions;
mapping(bytes32 => Request) public requests;

string private constant srcJsCode =
"const { createWalletClient, custom } = await import('npm:viem'); const { privateKeyToAccount } = await import('npm:viem/accounts'); const { polygonMumbai, avalancheFuji } = await import('npm:viem/chains'); const [contractAddress, ccipMessageId, sender, recipient, amount, srcChainSelector, dstChainSelector, token] = args; const chainSelectors = { '12532609583862916517': { url: `https://polygon-mumbai.infura.io/v3/${secrets.INFURA_API_KEY}`, chain: polygonMumbai, }, '14767482510784806043': { url: `https://avalanche-fuji.infura.io/v3/${secrets.INFURA_API_KEY}`, chain: avalancheFuji, }, }; const abi = [ { name: 'addUnconfirmedTX', type: 'function', inputs: [ { type: 'bytes32', name: 'ccipMessageId' }, { type: 'address', name: 'sender' }, { type: 'address', name: 'recipient' }, { type: 'uint256', name: 'amount' }, { type: 'uint64', name: 'srcChainSelector' }, { type: 'address', name: 'token' }, ], outputs: [], }, ]; try { const account = privateKeyToAccount('0x' + secrets.WALLET_PRIVATE_KEY); const walletClient = createWalletClient({ account, chain: chainSelectors[dstChainSelector].chain, transport: custom({ async request({ method, params }) { if (method === 'eth_chainId') return chainSelectors[dstChainSelector].chain.id; if (method === 'eth_estimateGas') return '0x3d090'; if (method === 'eth_maxPriorityFeePerGas') return '0x3b9aca00'; const response = await Functions.makeHttpRequest({ url: chainSelectors[dstChainSelector].url, method: 'post', headers: { 'Content-Type': 'application/json' }, data: { jsonrpc: '2.0', id: 1, method, params }, }); return response.data.result; }, }), }); const hash = await walletClient.writeContract({ abi, functionName: 'addUnconfirmedTX', address: contractAddress, args: [ccipMessageId, sender, recipient, amount, BigInt(srcChainSelector), token], gas: 1000000n, }); return Functions.encodeString(hash); } catch (err) { return Functions.encodeString('error'); }";
string private constant dstJsCode =
"const ethers = await import('npm:[email protected]'); const [srcContractAddress, messageId] = args; const params = { url: `https://polygon-mumbai.infura.io/v3/${secrets.INFURA_API_KEY}`, method: 'POST', headers: { 'Content-Type': 'application/json', }, data: { jsonrpc: '2.0', method: 'eth_getLogs', id: 1, params: [ { address: srcContractAddress, topics: [null, messageId], fromBlock: 'earliest', toBlock: 'latest', }, ], }, }; const response = await Functions.makeHttpRequest(params); const { data } = response; if (data?.error || !data?.result) { throw new Error('Error fetching logs'); } const abi = ['event CCIPSent(bytes32 indexed, address, address, address, uint256, uint64)']; const contract = new ethers.Interface(abi); const log = { topics: [ethers.id('CCIPSent(bytes32,address,address,address,uint256,uint64)'), data.result[0].topics[1]], data: data.result[0].data, }; const decodedLog = contract.parseLog(log); const croppedArgs = args.slice(1); for (let i = 0; i < decodedLog.args.length; i++) { if (decodedLog.args[i].toString().toLowerCase() !== croppedArgs[i].toString().toLowerCase()) { throw new Error('Message ID does not match the event log'); } } return Functions.encodeUint256(BigInt(messageId));";

Expand All @@ -36,22 +38,24 @@ contract CFunctions is FunctionsClient, ConfirmedOwner, IFunctions {
_;
}

modifier onlyInternalCCIPContract() {
if (msg.sender != internalCcipContract) {
revert NotCCIPContract(msg.sender);
}
_;
}

constructor(
address _router,
bytes32 _donId,
uint64 _subscriptionId,
uint64 _donHostedSecretsVersion,
address _externalCcipContract,
address payable _internalCcipContract,
uint64 _chainSelector
) FunctionsClient(_router) ConfirmedOwner(msg.sender) {
donId = _donId;
subscriptionId = _subscriptionId;
donHostedSecretsVersion = _donHostedSecretsVersion;
allowlist[msg.sender] = true;
externalCcipContract = _externalCcipContract;
internalCcipContract = _internalCcipContract;
conceroCCIP = ConceroCCIP(_internalCcipContract);
chainSelector = _chainSelector;
}

Expand Down Expand Up @@ -133,7 +137,7 @@ contract CFunctions is FunctionsClient, ConfirmedOwner, IFunctions {
requests[reqId].requestType = RequestType.checkTxSrc;
requests[reqId].isPending = true;

emit UnconfirmedTXAdded(ccipMessageId, sender, recipient, amount, token);
emit UnconfirmedTXAdded(ccipMessageId, sender, recipient, amount, token, srcChainSelector);
}

function sendRequest(string[] memory args, string memory jsCode) internal returns (bytes32) {
Expand Down Expand Up @@ -171,7 +175,32 @@ contract CFunctions is FunctionsClient, ConfirmedOwner, IFunctions {
if (address(conceroCCIP) == address(0)) {
revert("conceroCCIP address not set");
}

conceroCCIP.sendTokenToEoa(ccipMessageId, transaction.sender, transaction.recipient, transaction.token, transaction.amount);
}

function sendUnconfirmedTX(
bytes32 ccipMessageId,
address sender,
address recipient,
uint256 amount,
uint64 dstChainSelector,
address token
) external onlyInternalCCIPContract {
string[] memory args = new string[](8);
args[0] = Strings.toHexString(externalCcipContract);
args[1] = bytes32ToString(ccipMessageId);
args[2] = Strings.toHexString(sender);
args[3] = Strings.toHexString(recipient);
args[4] = Strings.toString(amount);
args[5] = Strings.toString(chainSelector);
args[6] = Strings.toString(dstChainSelector);
args[7] = Strings.toHexString(token);

bytes32 reqId = sendRequest(args, srcJsCode);

requests[reqId].requestType = RequestType.addUnconfirmedTxDst;
requests[reqId].isPending = true;

emit UnconfirmedTXSent(ccipMessageId, sender, recipient, amount, token, dstChainSelector);
}
}
10 changes: 8 additions & 2 deletions packages/hardhat/contracts/ConceroCCIP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ pragma solidity ^0.8.19;
import {CCIPInternal} from "./CCIPInternal.sol";
import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
import {CFunctions} from "./CFunctions.sol";

contract ConceroCCIP is CCIPInternal, ConfirmedOwner {
address internal internalFunctionContract;
CFunctions private cFunctions;

modifier onlyFunctionContract() {
if (msg.sender != internalFunctionContract) {
Expand All @@ -33,6 +35,7 @@ contract ConceroCCIP is CCIPInternal, ConfirmedOwner {

function setInternalFunctionContract(address _internalFunctionContract) external onlyOwner {
internalFunctionContract = _internalFunctionContract;
cFunctions = CFunctions(_internalFunctionContract);
}

function setDstConceroCCIPContract(uint64 _chainSelector, address _dstConceroCCIPContract) external onlyOwner {
Expand All @@ -50,9 +53,12 @@ contract ConceroCCIP is CCIPInternal, ConfirmedOwner {

require(isOK, "Transfer failed");

_sendTokenPayLink(_destinationChainSelector, _receiver, _token, _amount);
bytes32 ccipMessageId = _sendTokenPayLink(_destinationChainSelector, _receiver, _token, _amount);

// sendRequest() for trigger functions
if (address(cFunctions) == address(0)) {
revert("cFunctions address not set");
}
cFunctions.sendUnconfirmedTX(ccipMessageId, msg.sender, _receiver, _amount, _destinationChainSelector, _token);
}

function sendTokenToEoa(bytes32 _ccipMessageId, address _sender, address _recipient, address _token, uint256 _amount) external onlyFunctionContract {
Expand Down
4 changes: 3 additions & 1 deletion packages/hardhat/contracts/IConcero.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ interface ICCIP {
}

interface IFunctions {
event UnconfirmedTXAdded(bytes32 indexed ccipMessageId, address indexed sender, address indexed recipient, uint256 amount, address token);
event UnconfirmedTXAdded(bytes32 indexed ccipMessageId, address sender, address recipient, uint256 amount, address token, uint64 srcChainSelector);
event UnconfirmedTXSent(bytes32 indexed ccipMessageId, address sender, address recipient, uint256 amount, address token, uint64 dstChainSelector);
event TXConfirmed(bytes32 indexed ccipMessageId, address indexed sender, address indexed recipient, uint256 amount, address token);
event AllowlistUpdated(address indexed walletAddress, bool status);

error NotAllowed();
error TXAlreadyExists(bytes32 txHash, bool isConfirmed);
error UnexpectedRequestID(bytes32);
error NotCCIPContract(address);

struct Transaction {
bytes32 ccipMessageId;
Expand Down

0 comments on commit 0b7a435

Please sign in to comment.