We are starting with defining contract and dependencies. As access control we are choosing Ownable and also since it is UTS Token we need to import ERC20 implementation, here we are using one by openzeppelin
UTSBase is our main contract that should be inherited by any Token or Connector.
In this implementation we also used Ownable and ERC20.
After this step we need to override 3 functions: _mintTo, _burnFrom and _authorizeCall. We need to do it, because this token is mint/burn, so, internal logic should be of main bridging function should be done via mint/burn of underlying token.
function_burnFrom(address spender,address from,bytesmemory/* to */,uint256 amount,uint256 dstChainId,bytesmemory customPayload) internaloverridereturns(uint256 bridgedAmount) {if (from != spender) _spendAllowance(from, spender, amount);_update(from,address(0), amount);// This is just an example of how multi-bridge can be implemented.if (customPayload.length >0) {// {address} type array can be used here if the token will exist only in EVM-compatible chains. (bytes[] memory _receivers,uint256[] memory _amounts) = abi.decode(customPayload, (bytes[],uint256[]));// Some basic checks are added here:// 1. the {_receivers} and {_amounts} arrays lengths are the same// 2. the {_receivers} addresses are not empty// 3. the sum of {_amounts} is equal to {amount}if (_receivers.length != _amounts.length) revertUTSTokenWithLogicShowcase__E0();uint256 _amountsSum;for (uint256 i; _receivers.length > i; ++i) {if (_receivers[i].length ==0) revertUTSTokenWithLogicShowcase__E1(); _amountsSum += _amounts[i]; }if (amount != _amountsSum) revertUTSTokenWithLogicShowcase__E2(); ChainConfig memory config = _chainConfig[dstChainId];emitMultiBridged(spender, from, config.peerAddress, config.peerAddress, _receivers, _amounts, dstChainId); }return amount;}
In this implementation, we use customPayload as an option to transfer tokens to multiple receivers in different amounts just by one transaction. To do that, we define rules for how the raw bytes passed in customPayload should be encoded. In the _burnFrom function, we burn the tokens from the token holder in the provided amount and implement basic validations of the customPayload, if it is provided.
Also, it is necessary to implement tokens minting to the receiver or multiple receivers in the _mintTo function. Both possible scenarios need to be handled depending on whether a meaningful customPayload was provided. In this case, the raw bytes should be decoded, and the corresponding token _amounts should be minted to _receivers.
So, our contract is ready, now it can be deployed on networks and we can start bridging tokens between each chain.