diff --git a/contracts/RealmDiamond/facets/AlchemicaFacet.sol b/contracts/RealmDiamond/facets/AlchemicaFacet.sol index 900f5e95..55de91bc 100644 --- a/contracts/RealmDiamond/facets/AlchemicaFacet.sol +++ b/contracts/RealmDiamond/facets/AlchemicaFacet.sol @@ -27,6 +27,14 @@ contract AlchemicaFacet is Modifiers { event SurveyingRoundProgressed(uint256 indexed _newRound); + event TransferTokensWithGotchiId( + address indexed _sender, + uint256 indexed _gotchiId, + address _tokenAddress, + address _targetAddress, + uint256 _amount + ); + event TransferTokensToGotchi(address indexed _sender, uint256 indexed _gotchiId, address _tokenAddresses, uint256 _amount); error ERC20TransferFailed(string _tokenName); @@ -175,8 +183,8 @@ contract AlchemicaFacet is Modifiers { } function calculateTransferAmounts(uint256 _amount, uint256 _spilloverRate) internal pure returns (TransferAmounts memory) { - uint256 owner = (_amount * (bp - (_spilloverRate * 10**16))) / bp; - uint256 spill = (_amount * (_spilloverRate * 10**16)) / bp; + uint256 owner = (_amount * (bp - (_spilloverRate * 10 ** 16))) / bp; + uint256 spill = (_amount * (_spilloverRate * 10 ** 16)) / bp; return TransferAmounts(owner, spill); } @@ -188,11 +196,7 @@ contract AlchemicaFacet is Modifiers { /// @param _realmId Identifier of parcel to claim alchemica from /// @param _gotchiId Identifier of Aavegotchi to use for alchemica collecction/claiming /// @param _signature Message signature used for backend validation - function claimAvailableAlchemica( - uint256 _realmId, - uint256 _gotchiId, - bytes memory _signature - ) external gameActive { + function claimAvailableAlchemica(uint256 _realmId, uint256 _gotchiId, bytes memory _signature) external gameActive { //Check signature require( LibSignature.isValid(keccak256(abi.encode(_realmId, _gotchiId, s.lastClaimedAlchemica[_realmId])), _signature, s.backendPubKey), @@ -231,12 +235,7 @@ contract AlchemicaFacet is Modifiers { /// @param _gotchiId Identifier of parent ERC721 aavegotchi which alchemica is channeled to /// @param _lastChanneled The last time alchemica was channeled in this _realmId /// @param _signature Message signature used for backend validation - function channelAlchemica( - uint256 _realmId, - uint256 _gotchiId, - uint256 _lastChanneled, - bytes memory _signature - ) external gameActive { + function channelAlchemica(uint256 _realmId, uint256 _gotchiId, uint256 _lastChanneled, bytes memory _signature) external gameActive { AavegotchiDiamond diamond = AavegotchiDiamond(s.aavegotchiDiamond); //gotchi CANNOT have active listing for lending @@ -343,15 +342,32 @@ contract AlchemicaFacet is Modifiers { } } + function batchTransferAlchemicaWithGotchiIds(uint256[] calldata _gotchiIds, address[] calldata _targets, uint256[4][] calldata _amounts) external { + require(_gotchiIds.length == _amounts.length, "AlchemicaFacet: Mismatched array lengths"); + require(_targets.length == _amounts.length, "AlchemicaFacet: Mismatched array lengths"); + + IERC20Mintable[4] memory alchemicas = [ + IERC20Mintable(s.alchemicaAddresses[0]), + IERC20Mintable(s.alchemicaAddresses[1]), + IERC20Mintable(s.alchemicaAddresses[2]), + IERC20Mintable(s.alchemicaAddresses[3]) + ]; + + for (uint256 i = 0; i < _targets.length; i++) { + for (uint256 j = 0; j < _amounts[i].length; j++) { + if (_amounts[i][j] > 0) { + alchemicas[j].transferFrom(msg.sender, _targets[i], _amounts[i][j]); + emit TransferTokensWithGotchiId(msg.sender, _gotchiIds[i], s.alchemicaAddresses[j], _targets[i], _amounts[i][j]); + } + } + } + } + /// @notice Helper function to batch transfer alchemica to Aavegotchis /// @param _gotchiIds Array of Gotchi IDs /// @param _tokenAddresses Array of tokens to transfer /// @param _amounts Nested array of amounts to transfer. - function batchTransferTokensToGotchis( - uint256[] calldata _gotchiIds, - address[] calldata _tokenAddresses, - uint256[][] calldata _amounts - ) external { + function batchTransferTokensToGotchis(uint256[] calldata _gotchiIds, address[] calldata _tokenAddresses, uint256[][] calldata _amounts) external { require(_gotchiIds.length == _amounts.length, "AlchemicaFacet: Mismatched array lengths"); for (uint256 i = 0; i < _gotchiIds.length; i++) { @@ -393,11 +409,7 @@ contract AlchemicaFacet is Modifiers { } } - function _batchTransferTokens( - address[] memory _tokens, - uint256[] memory _amounts, - address _to - ) internal { + function _batchTransferTokens(address[] memory _tokens, uint256[] memory _amounts, address _to) internal { require(_tokens.length == _amounts.length, "Array legth mismatch"); require(_to != address(0), "Address Zero Transfer"); for (uint256 i; i < _tokens.length; i++) { @@ -414,11 +426,7 @@ contract AlchemicaFacet is Modifiers { } } - function batchTransferTokens( - address[][] calldata _tokens, - uint256[][] calldata _amounts, - address[] calldata _to - ) external { + function batchTransferTokens(address[][] calldata _tokens, uint256[][] calldata _amounts, address[] calldata _to) external { require(_tokens.length == _amounts.length, "Array length mismatch"); require(_to.length == _amounts.length, "Array length mismatch"); for (uint256 i; i < _to.length; i++) { diff --git a/scripts/realm/upgrades/upgrade-batchTransferAlchemicaWithGotchiIds.ts b/scripts/realm/upgrades/upgrade-batchTransferAlchemicaWithGotchiIds.ts new file mode 100644 index 00000000..10e1d5de --- /dev/null +++ b/scripts/realm/upgrades/upgrade-batchTransferAlchemicaWithGotchiIds.ts @@ -0,0 +1,47 @@ +import { run, ethers, network } from "hardhat"; +import { varsForNetwork } from "../../../constants"; +import { + convertFacetAndSelectorsToString, + DeployUpgradeTaskArgs, + FacetsAndAddSelectors, +} from "../../../tasks/deployUpgrade"; + +export async function upgradeBatchTransferAlchemicaWithGotchiIds() { + const diamondUpgrader = "0xa370f2ADd2A9Fba8759147995d6A0641F8d7C119"; + + const c = await varsForNetwork(ethers); + + const facets: FacetsAndAddSelectors[] = [ + { + facetName: "AlchemicaFacet", + addSelectors: [ + `function batchTransferAlchemicaWithGotchiIds(uint256[] calldata _gotchiIds, address[] calldata _targets, uint256[4][] calldata _amounts) external`, + ], + removeSelectors: [], + }, + ]; + + const joined = convertFacetAndSelectorsToString(facets); + + const args: DeployUpgradeTaskArgs = { + diamondUpgrader: diamondUpgrader, + diamondAddress: c.realmDiamond, + facetsAndAddSelectors: joined, + useLedger: true, + useMultisig: false, + initAddress: ethers.constants.AddressZero, + initCalldata: "0x", + }; + + await run("deployUpgrade", args); +} + +if (require.main === module) { + upgradeBatchTransferAlchemicaWithGotchiIds() + .then(() => process.exit(0)) + // .then(() => console.log('upgrade completed') /* process.exit(0) */) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/test/realm/testBatchTransferAlchemicaWithGotchiIds.ts b/test/realm/testBatchTransferAlchemicaWithGotchiIds.ts new file mode 100644 index 00000000..a3d44da1 --- /dev/null +++ b/test/realm/testBatchTransferAlchemicaWithGotchiIds.ts @@ -0,0 +1,57 @@ +import { impersonate } from "../../scripts/helperFunctions"; +import { ethers, network } from "hardhat"; +import { AlchemicaFacet } from "../../typechain"; +import { alchemica, maticVars } from "../../constants"; +import { expect } from "chai"; +import { upgradeBatchTransferAlchemicaWithGotchiIds } from "../../scripts/realm/upgrades/upgrade-batchTransferAlchemicaWithGotchiIds"; + +describe("Testing events on batchTransferTokensToGotchis", async function () { + const testAddress = "0x2c1a288353e136b9e4b467aadb307133fffeab25"; + const testDestAddress = "0xb7601193f559de56D67FB8e6a2AF219b05BD36c7"; + const testGotchiId = 24186; + const testAmounts = [ + ethers.utils.parseEther("0.01"), + ethers.utils.parseEther("0.02"), + ethers.utils.parseEther("0.03"), + ethers.utils.parseEther("0.01"), + ]; + + let alchemicaFacet: AlchemicaFacet; + + before(async function () { + this.timeout(20000000); + + await upgradeBatchTransferAlchemicaWithGotchiIds(); + + alchemicaFacet = (await ethers.getContractAt( + "AlchemicaFacet", + maticVars.realmDiamond + )) as AlchemicaFacet; + }); + + it("test upgrade", async function () { + alchemicaFacet = await impersonate( + testAddress, + alchemicaFacet, + ethers, + network + ); + const receipt = await ( + await alchemicaFacet.batchTransferAlchemicaWithGotchiIds( + [testGotchiId], + [testDestAddress], + [testAmounts] + ) + ).wait(); + const events = receipt.events.filter( + (event) => event.event === "TransferTokensWithGotchiId" + ); + expect(events.length).to.equal(4); + + events.forEach((event, i) => { + expect(event.args._tokenAddress).to.equal(alchemica[i]); + expect(event.args._targetAddress).to.equal(testDestAddress); + expect(event.args._amount).to.equal(testAmounts[i]); + }); + }); +});