All--or nearly all--of the Treasury Manager's functions use the _trySandboxedCall function to check if updated versions of themselves are available before they are executed.
The check is done using the function's selector, which identifies the function and its input parameters. If an updated version that corresponds to that selector is present, it will be executed instead.
What follows are two examples that demonstrate how to extend a Treasury Manager functionality.
Example of a New Function Limiting the Current One
Here, the Treasury Manager is using the standard onERC721Received function, which handles receiving ERC721s.
Let's say you want to update the ERC721Received function to inhibit the receival of ERC721s, so that the Treasury Manager, and therefore the Organization, can no longer receive any at all.
For this, setAdditionalFunction is used. As input, it takes the selector (bytes4), the server (address) and the log (bool) parameters.
In this case, the newServer contract contains the new onERC721Received function that inhibits the receival of ERC721s:
pragma solidity ^0.8.0;
contract TestContract {
function onERC721Received(address, address, uint256, bytes calldata b) external returns (bytes4) {
revert("ERC721 receive temporary not allowed");
}
}
The selector of the function is retrieved:
var selector = web3.utils.sha3("onERC721Received(address,address,uint256,bytes)").substring(0, 10) + utilities.voidBytes32.substring(10);
To be called correctly, the selector of the new function must be the same as the original one. Naturally, only the selector must be the same; the content of the function can be different.
Now, setAdditionalFunction can be called. Every time an ERC721 token arrives in the Treasury Manager, the new onERC721Received function will be automatically called, and the transaction reverts.
Here's an example of the entire integration code, with some useful checks:
pragma solidity ^0.8.0;
contract TestContract {
function onERC721Received(address, address, uint256, bytes calldata b) external returns (bytes4) {
revert("ERC721 receive temporary not allowed");
}
}`, 'TestContract');
var testContract = await new web3.eth.Contract(TestContract.abi).deploy({data : TestContract.bin}).send(blockchainConnection.getSendingOptions());
var selector = web3.utils.sha3("onERC721Received(address,address,uint256,bytes)").substring(0, 10) + utilities.voidBytes32.substring(10);
var transaction = await manager.methods.setAdditionalFunction(selector.substring(0, 10), testContract.options.address, true).send(organization.asActiveComponent);
var logs = (await web3.eth.getTransactionReceipt(transaction.transactionHash)).logs;
logs = logs.filter(it => it.topics[0] === web3.utils.sha3('AdditionalFunction(address,bytes4,address,address)') && it.topics[1] === selector && it.topics[2] === web3.eth.abi.encodeParameter("address", utilities.voidEthereumAddress) && it.topics[3] === web3.eth.abi.encodeParameter("address", testContract.options.address));
assert.equal(1, logs.length);
//the TreasuryManager can no longer accept ERC721 tokens
objectId = await erc721.methods.lastId().call();
await erc721.methods.mint(accounts[0]).send(blockchainConnection.getSendingOptions());
await catchCall(erc721.methods.safeTransferFrom(accounts[0], manager.options.address, objectId), "ERC721 receive temporary not allowed");
Example of a New Function Extending the Current One
Here, the Treasury Manager is using the standard onERC1155Received and onERC1155BatchReceived functions, which handle the receival of ERC1155s:
Let's say you want to update the onERC1155Received and onERC1155BatchReceived functions to have them emit events when the Treasury Manager receives ERC1155 tokens.
For this, setAdditionalFunction is used. As input, it takes the selector (bytes4), the server (address) and the log (bool) parameters.
To be called correctly, the selectors of the new functions must be the same as the original ones. Naturally, only the selectors must be the same; the content of the functions can be different.
Now, the setAdditionalFunction can be called. Every time an ERC1155 token arrives in the Treasury Manager, the SingleTransfer event is emitted (if the tokens are received via theonERC1155Received function) or the MultipleTransfer event is emitted (if the tokens are rececived via the onERC1155BatchReceived function.
Here's an example of the entire integration code, with some useful checks: