From ad4def757769ec73738dd3dfcfdc3bbc2dce36a9 Mon Sep 17 00:00:00 2001 From: BokkyPooBah Date: Tue, 10 Sep 2024 14:38:35 +1000 Subject: [PATCH] TokenAgentFactory v0.8.2 deployed to Sepolia --- README.md | 1 + contracts/TokenAgentFactory.sol | 52 +- ...426d5E4B6515E627Ff510424978eBe223c39C4.sol | 906 ++++++++++++++++++ docs/agent.js | 182 +++- testIt.out | 178 ++-- 5 files changed, 1181 insertions(+), 138 deletions(-) create mode 100644 deployed/TokenAgentFactory_v0.8.2_Sepolia_0xB6426d5E4B6515E627Ff510424978eBe223c39C4.sol diff --git a/README.md b/README.md index 7e0a20e..8c56daf 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ Your newly deployed Token Agent should appear ##### Deployments to Sepolia * v0.8.0 template [TokenAgent](https://sepolia.etherscan.io/address/0x0514e4402fe93b6ba0b014b30e5b715ed0943c25#code) and [TokenAgentFactory](https://sepolia.etherscan.io/address/0x598b17e44c3e8894dfcc9aaec16dad81756f5651#code) using [WETH](https://sepolia.etherscan.io/address/0x07391dbE03e7a0DEa0fce6699500da081537B6c3#code) - [deployed/TokenAgentFactorysol v0.8.0](deployed/TokenAgentFactory_v0.8.0_Sepolia_0x598b17E44c3e8894DfcC9aAec16DaD81756F5651.sol) * v0.8.1 template [TokenAgent](https://sepolia.etherscan.io/address/0x35e401362D24a2243b9a441542a4D4FFe50db1bF#code) and [TokenAgentFactory](https://sepolia.etherscan.io/address/0x81c9d0d4c60e6Ec7bb13879f703b113c930Cd914#code) using [WETH](https://sepolia.etherscan.io/address/0x07391dbE03e7a0DEa0fce6699500da081537B6c3#code) - [deployed/TokenAgentFactorysol v0.8.1](deployed/TokenAgentFactory_v0.8.1_Sepolia_0x81c9d0d4c60e6Ec7bb13879f703b113c930Cd914.sol) +* v0.8.1 template [TokenAgent](https://sepolia.etherscan.io/address/0x5446e959103b19e983848FB53d9fbD096eDb21A9#code) and [TokenAgentFactory](https://sepolia.etherscan.io/address/0xB6426d5E4B6515E627Ff510424978eBe223c39C4#code) using [WETH](https://sepolia.etherscan.io/address/0x07391dbE03e7a0DEa0fce6699500da081537B6c3#code) - [deployed/TokenAgentFactorysol v0.8.2](deployed/TokenAgentFactory_v0.8.2_Sepolia_0xB6426d5E4B6515E627Ff510424978eBe223c39C4.sol) ##### Notes This project is currently heavily under development. Clear your browser's LocalStorage and IndexedDB if this dapp is not operating as expected as the configuration data may have a new format. diff --git a/contracts/TokenAgentFactory.sol b/contracts/TokenAgentFactory.sol index 8b48bef..aec5c60 100644 --- a/contracts/TokenAgentFactory.sol +++ b/contracts/TokenAgentFactory.sol @@ -1,3 +1,7 @@ +/** + *Submitted for verification at Etherscan.io on 2024-09-10 +*/ + pragma solidity ^0.8.27; // ---------------------------------------------------------------------------- @@ -7,8 +11,8 @@ pragma solidity ^0.8.27; // // Deployed to Sepolia // - WETH 0x07391dbE03e7a0DEa0fce6699500da081537B6c3 -// - TokenAgent template -// - TokenAgentFactory +// - TokenAgent template 0x5446e959103b19e983848FB53d9fbD096eDb21A9 +// - TokenAgentFactory 0xB6426d5E4B6515E627Ff510424978eBe223c39C4 // // TODO: // - FILL for ERC-721/1155? @@ -258,12 +262,41 @@ contract NonReentrancy { /// @notice Token information contract TokenInfo { - mapping(Token => TokenType) tokenTypes; - function _supportsInterface(Token token, bytes4 _interface) internal view returns (bool b) { - try IERC165(Token.unwrap(token)).supportsInterface(_interface) returns (bool _b) { - b = _b; - } catch { + bytes4 constant InvalidID = 0xffffffff; + bytes4 constant ERC165ID = 0x01ffc9a7; + + function doesContractImplementInterface(address _contract, bytes4 _interfaceId) internal view returns (bool) { + uint success; + uint result; + (success, result) = _noThrowCall(_contract, ERC165ID); + if ((success == 0) || (result == 0)) { + return false; + } + (success, result) = _noThrowCall(_contract, InvalidID); + if ((success == 0) || (result !=0 )) { + return false; + } + (success, result) = _noThrowCall(_contract, _interfaceId); + if ((success == 1) && (result == 1)) { + return true; + } + return false; + } + function _noThrowCall(address _contract, bytes4 _interfaceId) view internal returns (uint success, uint result) { + bytes4 erc165ID = ERC165ID; + assembly { + let x := mload(0x40) // Find empty storage location using "free memory pointer" + mstore(x, erc165ID) // Place signature at beginning of empty storage + mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature + success := staticcall( + 30000, // 30k gas + _contract, // To addr + x, // Inputs are stored at location x + 0x24, // Inputs are 36 bytes long + x, // Store output over input (saves space) + 0x20) // Outputs are 32 bytes long + result := mload(x) // Load the result } } function _decimals(Token token) internal view returns (uint8 __d) { @@ -279,9 +312,9 @@ contract TokenInfo { } function _getTokenType(Token token) internal view returns (TokenType _tokenType) { if (Token.unwrap(token).code.length > 0) { - if (_supportsInterface(token, ERC721_INTERFACE)) { + if (doesContractImplementInterface(Token.unwrap(token), ERC721_INTERFACE)) { _tokenType = TokenType.ERC721; - } else if (_supportsInterface(token, ERC1155_INTERFACE)) { + } else if (doesContractImplementInterface(Token.unwrap(token), ERC1155_INTERFACE)) { _tokenType = TokenType.ERC1155; } else { if (_decimals(token) != type(uint8).max) { @@ -359,6 +392,7 @@ contract TokenAgent is TokenInfo, Owned, NonReentrancy { WETH public weth; Nonce public nonce; + mapping(Token => TokenType) tokenTypes; Offer[] public offers; event InternalTransfer(address indexed from, address indexed to, uint ethers, Unixtime timestamp); diff --git a/deployed/TokenAgentFactory_v0.8.2_Sepolia_0xB6426d5E4B6515E627Ff510424978eBe223c39C4.sol b/deployed/TokenAgentFactory_v0.8.2_Sepolia_0xB6426d5E4B6515E627Ff510424978eBe223c39C4.sol new file mode 100644 index 0000000..aec5c60 --- /dev/null +++ b/deployed/TokenAgentFactory_v0.8.2_Sepolia_0xB6426d5E4B6515E627Ff510424978eBe223c39C4.sol @@ -0,0 +1,906 @@ +/** + *Submitted for verification at Etherscan.io on 2024-09-10 +*/ + +pragma solidity ^0.8.27; + +// ---------------------------------------------------------------------------- +// TokenAgent with factory v0.8.2 testing +// +// https://github.com/bokkypoobah/TokenAgent +// +// Deployed to Sepolia +// - WETH 0x07391dbE03e7a0DEa0fce6699500da081537B6c3 +// - TokenAgent template 0x5446e959103b19e983848FB53d9fbD096eDb21A9 +// - TokenAgentFactory 0xB6426d5E4B6515E627Ff510424978eBe223c39C4 +// +// TODO: +// - FILL for ERC-721/1155? +// +// SPDX-License-Identifier: MIT +// +// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2024. The MIT Licence. +// ---------------------------------------------------------------------------- + +// import "hardhat/console.sol"; + + +/// @notice https://github.com/optionality/clone-factory/blob/32782f82dfc5a00d103a7e61a17a5dedbd1e8e9d/contracts/CloneFactory.sol +/* +The MIT License (MIT) + +Copyright (c) 2018 Murray Software, LLC. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +//solhint-disable max-line-length +//solhint-disable no-inline-assembly + +contract CloneFactory { + + function createClone(address target) internal returns (address result) { + bytes20 targetBytes = bytes20(target); + assembly { + let clone := mload(0x40) + mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) + mstore(add(clone, 0x14), targetBytes) + mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) + result := create(0, clone, 0x37) + } + } + + function isClone(address target, address query) internal view returns (bool result) { + bytes20 targetBytes = bytes20(target); + assembly { + let clone := mload(0x40) + mstore(clone, 0x363d3d373d3d3d363d7300000000000000000000000000000000000000000000) + mstore(add(clone, 0xa), targetBytes) + mstore(add(clone, 0x1e), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) + + let other := add(clone, 0x40) + extcodecopy(query, other, 0, 0x2d) + result := and( + eq(mload(clone), mload(other)), + eq(mload(add(clone, 0xd)), mload(add(other, 0xd))) + ) + } + } +} +// End CloneFactory.sol + + +interface IERC20 { + event Transfer(address indexed from, address indexed to, uint tokens); + event Approval(address indexed owner, address indexed spender, uint tokens); + + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function decimals() external view returns (uint8); + + function totalSupply() external view returns (uint); + function balanceOf(address owner) external view returns (uint balance); + function allowance(address owner, address spender) external view returns (uint remaining); + function transfer(address to, uint tokens) external returns (bool success); + function approve(address spender, uint tokens) external returns (bool success); + function transferFrom(address from, address to, uint tokens) external returns (bool success); +} + +interface WETH is IERC20 { + receive() external payable; + function deposit() external payable; + function withdraw(uint wad) external; +} + +interface IERC165 { + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} + +interface IERC721Partial { + function transferFrom(address from, address to, uint tokenId) external; +} + +interface IERC1155Partial { + function safeTransferFrom(address from, address to, uint id, uint amount, bytes calldata data) external; +} + +type Account is address; // 2^160 +type Count is uint16; // 2^16 = 65,536 +type Index is uint32; // 2^32 = 4,294,967,296 +type Nonce is uint24; // 2^24 = 16,777,216 +type Price is uint128; // 2^128 = 340, 282,366,920,938,463,463, 374,607,431,768,211,456 +type Token is address; // 2^160 +type TokenId is uint; // 2^256 = 115,792, 089,237,316,195,423,570, 985,008,687,907,853,269, 984,665,640,564,039,457, 584,007,913,129,639,936 +type TokenId16 is uint16; // 2^16 = 65,536 +type Tokens is uint128; // 2^128 = 340, 282,366,920,938,463,463, 374,607,431,768,211,456 +type Unixtime is uint40; // 2^40 = 1,099,511,627,776. For Unixtime, 1,099,511,627,776 seconds = 34865.285000507356672 years + +enum BuySell { BUY, SELL } +enum Execution { FILL, FILLORKILL } +enum TokenIdType { TOKENID256, TOKENID16 } +enum TokenType { UNKNOWN, ERC20, ERC721, ERC1155, INVALID } + +bytes4 constant ERC721_INTERFACE = 0x80ac58cd; +bytes4 constant ERC1155_INTERFACE = 0xd9b67a26; + +Token constant THEDAO = Token.wrap(0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413); + +library ArrayUtils { + function indexOfTokenIds(TokenId[] memory tokenIds, TokenId target) internal pure returns (uint) { + if (tokenIds.length > 0) { + uint left; + uint right = tokenIds.length - 1; + uint mid; + while (left <= right) { + mid = (left + right) / 2; + if (TokenId.unwrap(tokenIds[mid]) < TokenId.unwrap(target)) { + left = mid + 1; + } else if (TokenId.unwrap(tokenIds[mid]) > TokenId.unwrap(target)) { + if (mid < 1) { + break; + } + right = mid - 1; + } else { + return mid; + } + } + } + return type(uint).max; + } + function indexOfTokenId16s(TokenId16[] memory tokenIds, TokenId16 target) internal pure returns (uint) { + if (tokenIds.length > 0) { + uint left; + uint right = tokenIds.length - 1; + uint mid; + while (left <= right) { + mid = (left + right) / 2; + if (TokenId16.unwrap(tokenIds[mid]) < TokenId16.unwrap(target)) { + left = mid + 1; + } else if (TokenId16.unwrap(tokenIds[mid]) > TokenId16.unwrap(target)) { + if (mid < 1) { + break; + } + right = mid - 1; + } else { + return mid; + } + } + } + return type(uint).max; + } +} + +/// @notice Ownership +contract Owned { + Account public owner; + Account public pendingOwner; + + event OwnershipTransferStarted(Account indexed previousOwner, Account indexed newOwner, Unixtime timestamp); + event OwnershipTransferred(Account indexed previousOwner, Account indexed newOwner, Unixtime timestamp); + + error AlreadyInitialised(); + error NotOwner(); + error Owner(); + error NotNewOwner(); + + modifier onlyOwner { + if (msg.sender != Account.unwrap(owner)) { + revert NotOwner(); + } + _; + } + modifier notOwner { + if (msg.sender == Account.unwrap(owner)) { + revert Owner(); + } + _; + } + + function initOwned(Account _owner) internal { + if (Account.unwrap(owner) != address(0)) { + revert AlreadyInitialised(); + } + owner = _owner; + } + function transferOwnership(Account _pendingOwner) public onlyOwner { + pendingOwner = _pendingOwner; + emit OwnershipTransferStarted(owner, _pendingOwner, Unixtime.wrap(uint40(block.timestamp))); + } + function acceptOwnership() public { + if (msg.sender != Account.unwrap(pendingOwner)) { + revert NotNewOwner(); + } + emit OwnershipTransferred(owner, pendingOwner, Unixtime.wrap(uint40(block.timestamp))); + owner = pendingOwner; + pendingOwner = Account.wrap(address(0)); + } + function recoverTokens(IERC20 token, Tokens tokens) public onlyOwner { + if (address(token) == address(0)) { + payable(msg.sender).transfer((Tokens.unwrap(tokens) == 0 ? address(this).balance : Tokens.unwrap(tokens))); + } else { + token.transfer(msg.sender, Tokens.unwrap(tokens) == 0 ? token.balanceOf(address(this)) : Tokens.unwrap(tokens)); + } + } + function recoverERC721Token(IERC721Partial token, TokenId tokenId) public onlyOwner { + token.transferFrom(address(this), msg.sender, TokenId.unwrap(tokenId)); + } + function recoverERC1155Token(IERC1155Partial token, TokenId tokenId, Tokens tokens) public onlyOwner { + token.safeTransferFrom(address(this), msg.sender, TokenId.unwrap(tokenId), Tokens.unwrap(tokens), ""); + + } +} + +/// @notice Reentrancy guard +contract NonReentrancy { + modifier nonReentrant() { + assembly { + if tload(0) { revert(0, 0) } + tstore(0, 1) + } + _; + assembly { + tstore(0, 0) + } + } +} + +/// @notice Token information +contract TokenInfo { + + bytes4 constant InvalidID = 0xffffffff; + bytes4 constant ERC165ID = 0x01ffc9a7; + + function doesContractImplementInterface(address _contract, bytes4 _interfaceId) internal view returns (bool) { + uint success; + uint result; + (success, result) = _noThrowCall(_contract, ERC165ID); + if ((success == 0) || (result == 0)) { + return false; + } + (success, result) = _noThrowCall(_contract, InvalidID); + if ((success == 0) || (result !=0 )) { + return false; + } + (success, result) = _noThrowCall(_contract, _interfaceId); + if ((success == 1) && (result == 1)) { + return true; + } + return false; + } + function _noThrowCall(address _contract, bytes4 _interfaceId) view internal returns (uint success, uint result) { + bytes4 erc165ID = ERC165ID; + assembly { + let x := mload(0x40) // Find empty storage location using "free memory pointer" + mstore(x, erc165ID) // Place signature at beginning of empty storage + mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature + success := staticcall( + 30000, // 30k gas + _contract, // To addr + x, // Inputs are stored at location x + 0x24, // Inputs are 36 bytes long + x, // Store output over input (saves space) + 0x20) // Outputs are 32 bytes long + result := mload(x) // Load the result + } + } + function _decimals(Token token) internal view returns (uint8 __d) { + try IERC20(Token.unwrap(token)).decimals() returns (uint8 _d) { + __d = _d; + } catch { + if (Token.unwrap(token) == Token.unwrap(THEDAO)) { + return 16; + } else { + __d = type(uint8).max; + } + } + } + function _getTokenType(Token token) internal view returns (TokenType _tokenType) { + if (Token.unwrap(token).code.length > 0) { + if (doesContractImplementInterface(Token.unwrap(token), ERC721_INTERFACE)) { + _tokenType = TokenType.ERC721; + } else if (doesContractImplementInterface(Token.unwrap(token), ERC1155_INTERFACE)) { + _tokenType = TokenType.ERC1155; + } else { + if (_decimals(token) != type(uint8).max) { + _tokenType = TokenType.ERC20; + } else { + _tokenType = TokenType.INVALID; + } + } + } else { + _tokenType = TokenType.INVALID; + } + } + + function getTokenTypes(Token[] memory tokens) public view returns (TokenType[] memory _tokenTypes) { + _tokenTypes = new TokenType[](tokens.length); + for (uint i = 0; i < tokens.length; i++) { + _tokenTypes[i] = _getTokenType(tokens[i]); + } + } +} + +/// @notice User owned TokenAgent +contract TokenAgent is TokenInfo, Owned, NonReentrancy { + + struct AddOffer { + Token token; // 160 bits + BuySell buySell; // 8 bits + Unixtime expiry; // 40 bits + Count count; // 16 bits + Price[] prices; // token/WETH 18dp + TokenId[] tokenIds; // ERC-721/1155 + Tokens[] tokenss; // ERC-20/1155 + } + struct UpdateOffer { + Index index; // 160 bits + Unixtime expiry; // 40 bits + Count count; // 16 bits + Price[] prices; // token/WETH 18dp + TokenId[] tokenIds; // ERC-721/1155 + Tokens[] tokenss; // ERC-20/1155 + } + struct Offer { + Token token; // 160 bits + BuySell buySell; // 8 bits + Unixtime expiry; // 40 bits + Count count; // 16 bits + Nonce nonce; // 24 bits + TokenIdType tokenIdType; // 8 bits + Price[] prices; // token/WETH 18dp + TokenId16[] tokenId16s; // ERC-721/1155, when max[tokenIds...] < 2 ** 16 + TokenId[] tokenIds; // ERC-721/1155, when max[tokenIds...] >= 2 ** 16 + Tokens[] tokenss; // ERC-20/1155 + Tokens[] useds; // ERC-20 + } + struct OfferInfo { + uint index; + Token token; + TokenType tokenType; + BuySell buySell; + Unixtime expiry; + Count count; + Nonce nonce; + Price[] prices; + TokenId[] tokenIds; + Tokens[] tokenss; + Tokens[] useds; + } + struct TradeInput { + Index index; // 32 bits + Price price; // 128 bits min - ERC-20 max average when buying, min average when selling; ERC-721/1155 max total price when buying, min total price when selling + Execution execution; // 8 bits - Only for ERC-20 - FILL or FILLORKILL + TokenId[] tokenIds; + Tokens[] tokenss; + } + + WETH public weth; + Nonce public nonce; + mapping(Token => TokenType) tokenTypes; + Offer[] public offers; + + event InternalTransfer(address indexed from, address indexed to, uint ethers, Unixtime timestamp); + event Offered(Index index, Account indexed maker, Token indexed token, TokenType tokenType, BuySell buySell, Unixtime expiry, Count count, Nonce nonce, Price[] prices, TokenId[] tokenIds, Tokens[] tokenss, Unixtime timestamp); + event OfferUpdated(Index index, Account indexed maker, Token indexed token, TokenType tokenType, BuySell buySell, Unixtime expiry, Count count, Nonce nonce, Price[] prices, TokenId[] tokenIds, Tokens[] tokenss, Unixtime timestamp); + event OffersInvalidated(Nonce newNonce, Unixtime timestamp); + event Traded(Index index, Account indexed taker, Account indexed maker, Token indexed token, TokenType tokenType, BuySell makerBuySell, uint[] prices, uint[] tokenIds, uint[] tokenss, Price price, Unixtime timestamp); + + error CannotOfferWETH(); + error ExecutedAveragePriceGreaterThanSpecified(Price executedAveragePrice, Price tradeAveragePrice); + error ExecutedAveragePriceLessThanSpecified(Price executedAveragePrice, Price tradeAveragePrice); + error ExecutedTotalPriceGreaterThanSpecified(Price executedTotalPrice, Price tradeTotalPrice); + error ExecutedTotalPriceLessThanSpecified(Price executedTotalPrice, Price tradeTotalPrice); + error InsufficentEthersRemaining(uint ethersRequested, uint ethersRemaining); + error InsufficentTokensRemaining(Tokens tokensRequested, Tokens tokensRemaining); + error InsufficentCountRemaining(Count requested, Count remaining); + error InvalidInputData(string reason); + error InvalidOffer(Nonce offerNonce, Nonce currentNonce); + error InvalidIndex(Index index); + error InvalidToken(Token token); + error InvalidTokenId(TokenId tokenId); + error OfferExpired(Index index, Unixtime expiry); + error TokenIdsMustBeSortedWithNoDuplicates(); + + constructor() { + } + + function init(WETH _weth, Account owner) external { + super.initOwned(owner); + weth = _weth; + } + function invalidateOrders() external onlyOwner { + nonce = Nonce.wrap(Nonce.unwrap(nonce) + 1); + emit OffersInvalidated(nonce, Unixtime.wrap(uint40(block.timestamp))); + } + + // AddOffer: + // ERC-20 + // prices[price0], tokenIds[], tokenss[] + // prices[price0], tokenIds[], tokenss[tokens0, tokens1, ...] + // prices[price0, price1, ...], tokenIds[], tokenss[tokens0, tokens1, ...] + // ERC-721 + // prices[price0], tokenIds[], tokenss[] + // prices[price0], tokenIds[tokenId0, tokenId1, ...], tokenss[] + // prices[price0, price1, ...], tokenIds[tokenId0, tokenId1, ...], tokenss[] + // ERC-1155 + // prices[price0], tokenIds[], tokenss[] + // prices[price0], tokenIds[tokenId0, tokenId1, ...], tokenss[] + // prices[price0, price1, ...], tokenIds[tokenId0, tokenId1, ...], tokenss[tokens0, tokens1, ...] + function addOffers(AddOffer[] calldata inputs) external onlyOwner { + for (uint i = 0; i < inputs.length; i++) { + AddOffer memory input = inputs[i]; + TokenType tokenType = tokenTypes[input.token]; + if (Token.unwrap(input.token) == address(weth)) { + revert CannotOfferWETH(); + } + if (tokenType == TokenType.UNKNOWN) { + tokenType = _getTokenType(input.token); + if (tokenType == TokenType.INVALID) { + revert InvalidToken(input.token); + } + tokenTypes[input.token] = tokenType; + } + Offer storage offer = offers.push(); + offer.token = input.token; + offer.buySell = input.buySell; + offer.expiry = input.expiry; + offer.count = input.count; + offer.nonce = nonce; + if (input.prices.length == 0) { + revert InvalidInputData("all: prices array must contain at least one price"); + } else if (tokenType == TokenType.ERC20 && input.prices.length != 1 && input.tokenss.length != input.prices.length) { + revert InvalidInputData("ERC-20: tokenss array length must match prices array length"); + } else if (tokenType == TokenType.ERC721 && input.prices.length != 1 && input.tokenIds.length != input.prices.length) { + revert InvalidInputData("ERC-721: tokenIds array length must match prices array length"); + } else if (tokenType == TokenType.ERC1155 && input.tokenIds.length != input.tokenss.length) { + revert InvalidInputData("ERC-1155: tokenIds and tokenss array length must match"); + } else if (tokenType == TokenType.ERC1155 && input.prices.length != 1 && input.tokenIds.length != input.prices.length) { + revert InvalidInputData("ERC-1155: tokenIds and tokenss array length must match prices array length"); + } + offer.prices = input.prices; + if (tokenType == TokenType.ERC721 || tokenType == TokenType.ERC1155) { + if (input.tokenIds.length > 1) { + for (uint j = 1; j < input.tokenIds.length; j++) { + if (TokenId.unwrap(input.tokenIds[j - 1]) >= TokenId.unwrap(input.tokenIds[j])) { + revert TokenIdsMustBeSortedWithNoDuplicates(); + } + } + } + if (input.tokenIds.length > 0) { + uint maxTokenId; + for (uint j = 0; j < input.tokenIds.length; j++) { + if (maxTokenId < TokenId.unwrap(input.tokenIds[j])) { + maxTokenId = TokenId.unwrap(input.tokenIds[j]); + } + } + if (maxTokenId < 2 ** 16) { + offer.tokenIdType = TokenIdType.TOKENID16; + } + } + if (offer.tokenIdType == TokenIdType.TOKENID16) { + for (uint j = 0; j < input.tokenIds.length; j++) { + offer.tokenId16s.push(TokenId16.wrap(uint16(TokenId.unwrap(input.tokenIds[j])))); + } + } else { + offer.tokenIds = input.tokenIds; + } + } + if (tokenType == TokenType.ERC20 || tokenType == TokenType.ERC1155) { + offer.tokenss = input.tokenss; + for (uint j = 0; j < input.tokenss.length; j++) { + offer.useds.push(); + } + } + emit Offered(Index.wrap(uint32(offers.length - 1)), Account.wrap(msg.sender), offer.token, tokenType, offer.buySell, offer.expiry, offer.count, nonce, input.prices, input.tokenIds, input.tokenss, Unixtime.wrap(uint40(block.timestamp))); + } + } + + // Cannot update token and buySell + function updateOffers(UpdateOffer[] calldata inputs) external onlyOwner { + for (uint i = 0; i < inputs.length; i++) { + UpdateOffer memory input = inputs[i]; + uint index = Index.unwrap(input.index); + Offer storage offer = offers[index]; + offer.expiry = input.expiry; + offer.count = input.count; + offer.nonce = nonce; + TokenType tokenType = tokenTypes[offer.token]; + if (input.prices.length == 0) { + revert InvalidInputData("all: prices array must contain at least one price"); + } else if (tokenType == TokenType.ERC20 && input.prices.length != 1 && input.tokenss.length != input.prices.length) { + revert InvalidInputData("ERC-20: tokenss array length must match prices array length"); + } else if (tokenType == TokenType.ERC721 && input.prices.length != 1 && input.tokenIds.length != input.prices.length) { + revert InvalidInputData("ERC-721: tokenIds array length must match prices array length"); + } else if (tokenType == TokenType.ERC1155 && input.tokenIds.length != input.tokenss.length) { + revert InvalidInputData("ERC-1155: tokenIds and tokenss array length must match"); + } else if (tokenType == TokenType.ERC1155 && input.prices.length != 1 && input.tokenIds.length != input.prices.length) { + revert InvalidInputData("ERC-1155: tokenIds and tokenss array length must match prices array length"); + } + offer.prices = input.prices; + if (tokenType == TokenType.ERC721 || tokenType == TokenType.ERC1155) { + if (input.tokenIds.length > 1) { + for (uint j = 1; j < input.tokenIds.length; j++) { + if (TokenId.unwrap(input.tokenIds[j - 1]) >= TokenId.unwrap(input.tokenIds[j])) { + revert TokenIdsMustBeSortedWithNoDuplicates(); + } + } + } + if (input.tokenIds.length > 0) { + uint maxTokenId; + for (uint j = 0; j < input.tokenIds.length; j++) { + if (maxTokenId < TokenId.unwrap(input.tokenIds[j])) { + maxTokenId = TokenId.unwrap(input.tokenIds[j]); + } + } + if (maxTokenId < 2 ** 16) { + offer.tokenIdType = TokenIdType.TOKENID16; + } + } + if (offer.tokenIdType == TokenIdType.TOKENID16) { + for (uint j = 0; j < input.tokenIds.length; j++) { + offer.tokenId16s.push(TokenId16.wrap(uint16(TokenId.unwrap(input.tokenIds[j])))); + } + } else { + offer.tokenIds = input.tokenIds; + } + } + if (tokenType == TokenType.ERC20 || tokenType == TokenType.ERC1155) { + offer.tokenss = input.tokenss; + } + emit OfferUpdated(Index.wrap(uint32(index)), Account.wrap(msg.sender), offer.token, tokenType, offer.buySell, offer.expiry, offer.count, nonce, input.prices, input.tokenIds, input.tokenss, Unixtime.wrap(uint40(block.timestamp))); + } + } + + // TradeInput: + // ERC-20 + // tokenIds[], tokenss[tokens0] + // ERC-721 + // tokenIds[tokenId0, tokenId1, ...], tokenss[] + // ERC-1155 + // tokenIds[tokenId0, tokenId1, ...], tokenss[tokens0, tokens1, ...] + function trade(TradeInput[] calldata inputs, bool paymentsInEth) external payable nonReentrant notOwner { + uint totalEth = msg.value; + if (totalEth > 0) { + emit InternalTransfer(msg.sender, address(this), totalEth, Unixtime.wrap(uint40(block.timestamp))); + } + uint takerToOwnerTotal; + uint ownerToTakerTotal; + for (uint i = 0; i < inputs.length; i++) { + TradeInput memory input = inputs[i]; + if (Index.unwrap(input.index) >= offers.length) { + revert InvalidIndex(input.index); + } + Offer storage offer = offers[Index.unwrap(input.index)]; + TokenType tokenType = tokenTypes[offer.token]; + if (Nonce.unwrap(offer.nonce) != Nonce.unwrap(nonce)) { + revert InvalidOffer(offer.nonce, nonce); + } + if (Unixtime.unwrap(offer.expiry) != 0 && block.timestamp > Unixtime.unwrap(offer.expiry)) { + revert OfferExpired(input.index, offer.expiry); + } + uint price; + uint[] memory prices_; + uint[] memory tokenIds_; + uint[] memory tokenss_; + if (tokenType == TokenType.ERC20) { + if (input.tokenss.length != 1) { + revert InvalidInputData("Expecting single tokens input"); + } + uint tokens = uint(Tokens.unwrap(input.tokenss[0])); + uint totalTokens; + uint totalWETHTokens; + prices_ = new uint[](offer.prices.length); + tokenIds_ = new uint[](0); + tokenss_ = new uint[](offer.prices.length); + uint k; + for (uint j = 0; j < offer.prices.length && tokens > 0; j++) { + uint _price = Price.unwrap(offer.prices[j]); + if (Tokens.unwrap(offer.tokenss[j]) > Tokens.unwrap(offer.useds[j])) { + uint remaining = Tokens.unwrap(offer.tokenss[j]) - Tokens.unwrap(offer.useds[j]); + if (tokens >= remaining) { + totalTokens += remaining; + totalWETHTokens += remaining * _price / 10**18; + offer.useds[j] = Tokens.wrap(Tokens.unwrap(offer.useds[j]) + uint128(remaining)); + prices_[k] = _price; + tokenss_[k] = remaining; + tokens -= remaining; + } else { + totalTokens += tokens; + totalWETHTokens += tokens * _price / 10**18; + offer.useds[j] = Tokens.wrap(Tokens.unwrap(offer.useds[j]) + uint128(tokens)); + prices_[k] = _price; + tokenss_[k] = tokens; + tokens = 0; + } + k++; + } + } + if (input.execution == Execution.FILLORKILL && totalTokens < uint(Tokens.unwrap(input.tokenss[0]))) { + revert InsufficentTokensRemaining(input.tokenss[0], Tokens.wrap(uint128(totalTokens))); + } + if (totalTokens > 0) { + price = totalWETHTokens * 10**18 / totalTokens; + if (offer.buySell == BuySell.BUY) { + if (price < Price.unwrap(input.price)) { + revert ExecutedAveragePriceLessThanSpecified(Price.wrap(uint128(price)), input.price); + } + IERC20(Token.unwrap(offer.token)).transferFrom(msg.sender, Account.unwrap(owner), totalTokens); + ownerToTakerTotal += totalWETHTokens; + } else { + if (price > Price.unwrap(input.price)) { + revert ExecutedAveragePriceGreaterThanSpecified(Price.wrap(uint128(price)), input.price); + } + takerToOwnerTotal += totalWETHTokens; + IERC20(Token.unwrap(offer.token)).transferFrom(Account.unwrap(owner), msg.sender, totalTokens); + } + } + } else if (tokenType == TokenType.ERC721) { + if (Count.unwrap(offer.count) != type(uint16).max) { + if (Count.unwrap(offer.count) < input.tokenIds.length) { + revert InsufficentCountRemaining(Count.wrap(uint16(input.tokenIds.length)), offer.count); + } + offer.count = Count.wrap(Count.unwrap(offer.count) - uint16(input.tokenIds.length)); + } + prices_ = new uint[](input.tokenIds.length); + tokenIds_ = new uint[](input.tokenIds.length); + tokenss_ = new uint[](0); + for (uint j = 0; j < input.tokenIds.length; j++) { + uint p; + if (offer.tokenIds.length > 0) { + uint k; + if (offer.tokenIdType == TokenIdType.TOKENID16) { + if (TokenId.unwrap(input.tokenIds[j]) < 2 ** 16) { + k = ArrayUtils.indexOfTokenId16s(offer.tokenId16s, TokenId16.wrap(uint16(TokenId.unwrap(input.tokenIds[j])))); + } else { + k = type(uint).max; + } + } else { + k = ArrayUtils.indexOfTokenIds(offer.tokenIds, input.tokenIds[j]); + } + if (k == type(uint).max) { + revert InvalidTokenId(input.tokenIds[j]); + } + if (offer.prices.length == offer.tokenIds.length) { + p = Price.unwrap(offer.prices[k]); + } else { + p = Price.unwrap(offer.prices[0]); + } + } else { + p = Price.unwrap(offer.prices[0]); + } + prices_[j] = p; + tokenIds_[j] = TokenId.unwrap(input.tokenIds[j]); + price += p; + if (offer.buySell == BuySell.BUY) { + IERC721Partial(Token.unwrap(offer.token)).transferFrom(msg.sender, Account.unwrap(owner), TokenId.unwrap(input.tokenIds[j])); + } else { + IERC721Partial(Token.unwrap(offer.token)).transferFrom(Account.unwrap(owner), msg.sender, TokenId.unwrap(input.tokenIds[j])); + } + } + if (offer.buySell == BuySell.BUY) { + if (price < Price.unwrap(input.price)) { + revert ExecutedTotalPriceLessThanSpecified(Price.wrap(uint128(price)), input.price); + } + ownerToTakerTotal += price; + } else { + if (price > Price.unwrap(input.price)) { + revert ExecutedTotalPriceGreaterThanSpecified(Price.wrap(uint128(price)), input.price); + } + takerToOwnerTotal += price; + } + } else if (tokenType == TokenType.ERC1155) { + if (input.tokenIds.length != input.tokenss.length) { + revert InvalidInputData("tokenIds must have the same length as tokenss"); + } + uint totalCount; + for (uint j = 0; j < input.tokenss.length; j++) { + totalCount += Tokens.unwrap(input.tokenss[j]); + } + if (Count.unwrap(offer.count) < totalCount) { + revert InsufficentCountRemaining(Count.wrap(uint16(totalCount)), offer.count); + } + if (Count.unwrap(offer.count) != type(uint16).max) { + offer.count = Count.wrap(Count.unwrap(offer.count) - uint16(totalCount)); + } + prices_ = new uint[](input.tokenIds.length); + tokenIds_ = new uint[](input.tokenIds.length); + tokenss_ = new uint[](input.tokenIds.length); + for (uint j = 0; j < input.tokenIds.length; j++) { + uint p; + if (offer.tokenIds.length > 0) { + uint k; + if (offer.tokenIdType == TokenIdType.TOKENID16) { + if (TokenId.unwrap(input.tokenIds[j]) < 2 ** 16) { + k = ArrayUtils.indexOfTokenId16s(offer.tokenId16s, TokenId16.wrap(uint16(TokenId.unwrap(input.tokenIds[j])))); + } else { + k = type(uint).max; + } + } else { + k = ArrayUtils.indexOfTokenIds(offer.tokenIds, input.tokenIds[j]); + } + if (k == type(uint).max) { + revert InvalidTokenId(input.tokenIds[j]); + } + if (Tokens.unwrap(offer.useds[k]) + Tokens.unwrap(input.tokenss[j]) > Tokens.unwrap(offer.tokenss[k])) { + revert InsufficentCountRemaining(Count.wrap(uint16(Tokens.unwrap(input.tokenss[j]))), Count.wrap(uint16(Tokens.unwrap(offer.tokenss[k])))); + } + offer.useds[k] = Tokens.wrap(Tokens.unwrap(offer.useds[k]) + uint128(Tokens.unwrap(input.tokenss[j]))); + if (offer.prices.length == offer.tokenIds.length) { + p = Price.unwrap(offer.prices[k]); + } else { + p = Price.unwrap(offer.prices[0]); + } + } else { + p = Price.unwrap(offer.prices[0]); + } + prices_[j] = p; + tokenIds_[j] = uint(TokenId.unwrap(input.tokenIds[j])); + tokenss_[j] = uint(Tokens.unwrap(input.tokenss[j])); + price += p * uint(Tokens.unwrap(input.tokenss[j])); + if (offer.buySell == BuySell.BUY) { + IERC1155Partial(Token.unwrap(offer.token)).safeTransferFrom(msg.sender, Account.unwrap(owner), TokenId.unwrap(input.tokenIds[j]), Tokens.unwrap(input.tokenss[j]), ""); + } else { + IERC1155Partial(Token.unwrap(offer.token)).safeTransferFrom(Account.unwrap(owner), msg.sender, TokenId.unwrap(input.tokenIds[j]), Tokens.unwrap(input.tokenss[j]), ""); + } + } + if (offer.buySell == BuySell.BUY) { + if (price < Price.unwrap(input.price)) { + revert ExecutedTotalPriceLessThanSpecified(Price.wrap(uint128(price)), input.price); + } + ownerToTakerTotal += price; + } else { + if (price > Price.unwrap(input.price)) { + revert ExecutedTotalPriceGreaterThanSpecified(Price.wrap(uint128(price)), input.price); + } + takerToOwnerTotal += price; + } + } + emit Traded(input.index, Account.wrap(msg.sender), owner, offer.token, tokenType, offer.buySell, prices_, tokenIds_, tokenss_, Price.wrap(uint128(price)), Unixtime.wrap(uint40(block.timestamp))); + } + if (takerToOwnerTotal < ownerToTakerTotal) { + uint diff = ownerToTakerTotal - takerToOwnerTotal; + if (paymentsInEth) { + weth.transferFrom(Account.unwrap(owner), address(this), diff); + weth.withdraw(diff); + emit InternalTransfer(address(this), msg.sender, diff, Unixtime.wrap(uint40(block.timestamp))); + payable(msg.sender).transfer(diff); + } else { + weth.transferFrom(Account.unwrap(owner), msg.sender, diff); + } + } else if (takerToOwnerTotal > ownerToTakerTotal){ + uint diff = takerToOwnerTotal - ownerToTakerTotal; + if (paymentsInEth) { + if (diff > totalEth) { + revert InsufficentEthersRemaining(diff, totalEth); + } + totalEth -= diff; + emit InternalTransfer(address(this), address(weth), diff, Unixtime.wrap(uint40(block.timestamp))); + weth.deposit{value: diff}(); + weth.transfer(Account.unwrap(owner), diff); + } else { + weth.transferFrom(msg.sender, Account.unwrap(owner), diff); + } + } + if (totalEth > 0) { + emit InternalTransfer(address(this), msg.sender, totalEth, Unixtime.wrap(uint40(block.timestamp))); + payable(msg.sender).transfer(totalEth); + } + } + + receive() external payable { + } + + function getOffersInfo(uint from, uint to) public view returns (OfferInfo[] memory results) { + uint start = from < offers.length ? from : offers.length; + uint end = to < offers.length ? to : offers.length; + results = new OfferInfo[](end - start); + uint k; + for (uint i = start; i < end; i++) { + if (i < offers.length) { + Offer memory offer = offers[i]; + TokenId[] memory tokenIds; + if (offer.tokenIdType == TokenIdType.TOKENID16) { + tokenIds = new TokenId[](offer.tokenId16s.length); + for (uint j = 0; j < offer.tokenId16s.length; j++) { + tokenIds[j] = TokenId.wrap(uint(TokenId16.unwrap(offer.tokenId16s[j]))); + } + } else { + tokenIds = offer.tokenIds; + } + results[k++] = OfferInfo(i, offer.token, tokenTypes[offer.token], offer.buySell, offer.expiry, offer.count, offer.nonce, offer.prices, tokenIds, offer.tokenss, offer.useds); + } + } + } +} + +/// @notice TokenAgent factory +contract TokenAgentFactory is CloneFactory { + + struct TokenAgentRecord { + TokenAgent tokenAgent; + Index indexByOwner; + } + struct TokenAgentInfo { + Index index; + Index indexByOwner; + TokenAgent tokenAgent; + Account owner; + } + + WETH public weth; + TokenAgent public tokenAgentTemplate; + TokenAgentRecord[] public tokenAgentRecords; + mapping(Account => Index[]) public tokenAgentIndicesByOwners; + mapping(TokenAgent => Index) public tokenAgentIndex; + + event NewTokenAgent(TokenAgent indexed tokenAgent, Account indexed owner, Index indexed index, Index indexByOwner, Unixtime timestamp); + + error NotInitialised(); + error GoAway(); + + constructor() { + tokenAgentTemplate = new TokenAgent(); + } + + function init(WETH _weth, TokenAgent _tokenAgentTemplate) public { + if (address(weth) != address(0)) { + revert GoAway(); + } + weth = _weth; + tokenAgentTemplate = _tokenAgentTemplate; + } + + function newTokenAgent() public { + if (address(weth) == address(0)) { + revert NotInitialised(); + } + TokenAgent tokenAgent = TokenAgent(payable(createClone(address(tokenAgentTemplate)))); + tokenAgent.init(weth, Account.wrap(msg.sender)); + tokenAgentRecords.push(TokenAgentRecord(tokenAgent, Index.wrap(uint32(tokenAgentIndicesByOwners[Account.wrap(msg.sender)].length)))); + tokenAgentIndicesByOwners[Account.wrap(msg.sender)].push(Index.wrap(uint32(tokenAgentRecords.length - 1))); + emit NewTokenAgent(tokenAgent, Account.wrap(msg.sender), Index.wrap(uint32(tokenAgentRecords.length - 1)), Index.wrap(uint32(tokenAgentIndicesByOwners[Account.wrap(msg.sender)].length - 1)), Unixtime.wrap(uint40(block.timestamp))); + } + + function tokenAgentsLength() public view returns (uint) { + return tokenAgentRecords.length; + } + function tokenAgentsByOwnerLength(Account owner) public view returns (uint) { + return tokenAgentIndicesByOwners[owner].length; + } + function getTokenAgentsInfo(uint from, uint to) public view returns (TokenAgentInfo[] memory results) { + uint start = from < tokenAgentRecords.length ? from : tokenAgentRecords.length; + uint end = to < tokenAgentRecords.length ? to : tokenAgentRecords.length; + results = new TokenAgentInfo[](end - start); + uint k; + for (uint i = start; i < end; i++) { + results[k++] = TokenAgentInfo(Index.wrap(uint32(i)), tokenAgentRecords[i].indexByOwner, tokenAgentRecords[i].tokenAgent, tokenAgentRecords[i].tokenAgent.owner()); + } + } + function getTokenAgentsByOwnerInfo(Account owner, uint from, uint to) public view returns (TokenAgentInfo[] memory results) { + Index[] memory indices = tokenAgentIndicesByOwners[owner]; + uint start = from < indices.length ? from : indices.length; + uint end = to < indices.length ? to : indices.length; + results = new TokenAgentInfo[](end - start); + uint k; + for (uint i = start; i < end; i++) { + uint index = Index.unwrap(indices[i]); + results[k++] = TokenAgentInfo(Index.wrap(uint32(index)), Index.wrap(uint32(i)), tokenAgentRecords[index].tokenAgent, tokenAgentRecords[index].tokenAgent.owner()); + } + } +} diff --git a/docs/agent.js b/docs/agent.js index f9739cf..9b90945 100644 --- a/docs/agent.js +++ b/docs/agent.js @@ -84,14 +84,14 @@ const Agent = { - + No Token contracts contracts with transfers permitted
- {{ index }}. {{ item.tokenContract.substring(0, 8) + '...' + item.tokenContract.slice(-6) + ' ' + item.symbol + ' ' + item.name + ' ' + (item.decimals != null ? parseInt(item.decimals) : '') }} + {{ index }}. {{ item.tokenContract.substring(0, 8) + '...' + item.tokenContract.slice(-6) + ' ' + item.symbol + ' ' + item.name + ' ' + (item.decimals != null ? parseInt(item.decimals) : '') }}
@@ -102,9 +102,6 @@ const Agent = {
- @@ -116,17 +113,18 @@ const Agent = {
- + - - Add Offer + + + + Add Offer + - - - -
@@ -253,6 +231,7 @@ const Agent = { offers: [], token: null, type: null, + symbol: null, decimals: null, buySell: 0, expiry: null, @@ -268,7 +247,7 @@ const Agent = { currentPage: 1, pageSize: 10, sortOption: 'ownertokenagentasc', - version: 4, + version: 5, }, buySellOptions: [ { value: 0, text: 'Buy' }, @@ -276,7 +255,7 @@ const Agent = { ], pricing20Options: [ { value: 0, text: 'Single price without limit' }, - { value: 1, text: 'Single price with limit', disabled: true }, + { value: 1, text: 'Single price with limit' }, { value: 1, text: 'Multiple prices and limits', disabled: true }, ], sortOptions: [ @@ -352,13 +331,36 @@ const Agent = { }, addOffersFeedback() { + if (!this.settings.tokenAgentAddress || !this.validAddress(this.settings.tokenAgentAddress)) { + return "Enter token agent address"; + } + if (!this.settings.addOffers.token || !this.validAddress(this.settings.addOffers.token)) { + return "Enter token contract address"; + } if (this.settings.addOffers.type == 20) { - if (this.settings.addOffers.pricing == 0 && parseFloat(this.settings.addOffers.price) > 0) { - return null; + if (this.settings.addOffers.pricing == 0) { + if (this.validNumber(this.settings.addOffers.price, 18)) { + return null; + } else { + return "Invalid price"; + } } - return "Only single price without limit supported" + if (this.settings.addOffers.pricing == 1) { + if (this.validNumber(this.settings.addOffers.price, 18)) { + } else { + return "Invalid price"; + } + if (this.validNumber(this.settings.addOffers.tokens, this.settings.addOffers.decimals)) { + return null; + } else { + return "Invalid tokens"; + } + } + return "Only single price with or without tokens limit supported" + } else if (this.settings.addOffers.type == 721 || this.settings.addOffers.type == 1155) { + return "ERC-721/1155 not supported yet"; } - return "ERC-721/1155 not supported yet"; + return null; }, totalRegistryEntries() { @@ -421,18 +423,105 @@ const Agent = { console.log(now() + " INFO Agent:methods.addOffer - settings.addOffers: " + JSON.stringify(this.settings.addOffers, null, 2)); const provider = new ethers.providers.Web3Provider(window.ethereum); const network = this.chainId && NETWORKS[this.chainId.toString()] || {}; + const contract = new ethers.Contract(this.settings.tokenAgentAddress, network.tokenAgent.abi, provider); + // // TestParameters.sol + // const contract = new ethers.Contract("0x05c2fcd4aE9CC26AaEEbCEE1F3B44780426d0c3b", network.tokenAgent.abi, provider); + const contractWithSigner = contract.connect(provider.getSigner()); - if (this.settings.addOffers.type == 20) { - if (this.settings.addOffers.pricing == 0 && parseFloat(this.settings.addOffers.price) > 0) { - console.log(now() + " INFO Agent:methods.addOffer - ERC-20 Single price without limit - price: " + this.settings.addOffers.price); + if (network.tokenAgentFactory) { + if (this.settings.addOffers.type == 20) { + let prices = []; + let tokens = []; + if (this.settings.addOffers.pricing == 0) { + console.log(now() + " INFO Agent:methods.addOffer - ERC-20 Single price without limit - price: " + this.settings.addOffers.price); + prices = [ethers.utils.parseUnits(this.settings.addOffers.price, 18).toString()]; + } else if (this.settings.addOffers.pricing == 1) { + console.log(now() + " INFO Agent:methods.addOffer - ERC-20 Single price with limit - price: " + this.settings.addOffers.price + ", tokens: " + this.settings.addOffers.tokens); + prices = [ethers.utils.parseUnits(this.settings.addOffers.price, 18).toString()]; + tokens = [ethers.utils.parseUnits(this.settings.addOffers.tokens, this.settings.addOffers.decimals).toString()]; + } else { + console.log(now() + " INFO Agent:methods.addOffer - ERC-20 Multiple prices with limits - UNSUPPORTED"); + } + console.log("prices: " + JSON.stringify(prices)); + console.log("tokens: " + JSON.stringify(tokens)); + if (prices.length > 0) { + // const payload = [ + // [ + // this.settings.addOffers.token, + // 0, // parseInt(this.settings.addOffers.buySell), + // 0, // "2041432206", // Sat Sep 09 2034 16:30:06 GMT+0000 + // 0, + // prices, + // [], + // tokens, + // ], + // ]; + + // const payload = [["0x7439E9Bb6D8a84dd3A23fe621A30F95403F87fB9","0","0","0",["123"],[],[]]]; + // TestToadz + const payload = [["0x8b73448426797099b6b9a96c4343f528bbAfc55e","0","0","0",["123"],[456],[]]]; + // const payload = [ + // { token: "0x7439E9Bb6D8a84dd3A23fe621A30F95403F87fB9", buySell: "0", expiry: "0", count: "0", prices: [], tokenIds: [], tokenss: [] }, + // ]; + // function addOffers(AddOffer[] calldata inputs) external onlyOwner { + // "84233ef3": "addOffers((address,uint8,uint40,uint16,uint128[],uint256[],uint128[])[])", + + // const abi = ethers.utils.defaultAbiCoder; + // const params = abi.encode( + // ["(address,uint8,uint40,uint16,uint128[],uint256[],uint128[])[]"], // encode as address array + // payload); // array to encode + // console.log(now() + " INFO Agent:methods.addOffer - params: " + JSON.stringify(params)); + try { + console.log(now() + " INFO Agent:methods.addOffer - payload: " + JSON.stringify(payload)); + const tx = await contractWithSigner.addOffers(payload, { gasLimit: 500000 }); + // struct AddOffer { + // Token token; // 160 bits + // BuySell buySell; // 8 bits + // Unixtime expiry; // 40 bits + // Count count; // 16 bits + // Price[] prices; // token/WETH 18dp + // TokenId[] tokenIds; // ERC-721/1155 + // Tokens[] tokenss; // ERC-20/1155 + // } + // const tx = { hash: "blah" }; + console.log(now() + " INFO Agent:methods.addOffer - tx: " + JSON.stringify(tx)); + const h = this.$createElement; + const vNodesMsg = h( + 'p', + { class: ['text-left', 'mb-0'] }, + [ + h('a', { attrs: { href: this.explorer + 'tx/' + tx.hash, target: '_blank' } }, tx.hash.substring(0, 20) + '...' + tx.hash.slice(-18)), + h('br'), + h('br'), + 'Resync after this tx has been included', + ] + ); + this.$bvToast.toast([vNodesMsg], { + title: 'Transaction submitted', + autoHideDelay: 5000, + }); + this.$refs['modalnewtokenagent'].hide(); + this.settings.newTokenAgent.show = false; + this.saveSettings(); + } catch (e) { + console.log(now() + " ERROR Agent:methods.addOffer: " + JSON.stringify(e)); + this.$bvToast.toast(`${e.message}`, { + title: 'Error!', + autoHideDelay: 5000, + }); + } + + } + } else { + console.log(now() + " INFO Agent:methods.addOffer - ERC-721/1155 - UNSUPPORTED"); } } - return; + if (network.tokenAgentFactory) { const contract = new ethers.Contract(network.tokenAgentFactory.address, network.tokenAgentFactory.abi, provider); const contractWithSigner = contract.connect(provider.getSigner()); @@ -468,6 +557,19 @@ const Agent = { } }, + validNumber(n, d) { + if (n && d != null) { + // console.log(now() + " DEBUG Agent:methods.validNumber - n: " + n + ", d: " + d); + try { + const n_ = ethers.utils.parseUnits(n, d); + // console.log(now() + " DEBUG Agent:methods.validNumber - n_: " + n_.toString()); + return true; + } catch (e) { + } + } + return false; + }, + validAddress(a) { if (a) { try { diff --git a/testIt.out b/testIt.out index d432d6e..640b712 100644 --- a/testIt.out +++ b/testIt.out @@ -2,99 +2,99 @@ TokenAgentFactory Deploy TokenAgentFactory And TokenAgent - * accounts[0]->TokenAgentFactory.deploy() => 0x5FC8d326 - gasUsed: 5,404,883 0.005404883Ξ 13.51 USD @ 1.0 gwei 2500.00 ETH/USD - * accounts[0]->tokenAgentFactory.newTokenAgent() => 0x23dB4a08 - gasUsed: 187,021 0.000187021Ξ 0.47 USD @ 1.0 gwei 2500.00 ETH/USD - * accounts[1]->tokenAgentFactory.newTokenAgent() => 0x8EFa1819 - gasUsed: 189,821 0.000189821Ξ 0.47 USD @ 1.0 gwei 2500.00 ETH/USD - * accounts[2]->tokenAgentFactory.newTokenAgent() => 0x6743E5c6 - gasUsed: 189,821 0.000189821Ξ 0.47 USD @ 1.0 gwei 2500.00 ETH/USD - * accounts[3]->tokenAgentFactory.newTokenAgent() => 0xA14d9C7a - gasUsed: 189,821 0.000189821Ξ 0.47 USD @ 1.0 gwei 2500.00 ETH/USD + * accounts[0]->TokenAgentFactory.deploy() => 0x5FC8d326 - gasUsed: 5,415,653 0.005415653Ξ 13.54 USD @ 1.0 gwei 2500.00 ETH/USD + * accounts[0]->tokenAgentFactory.newTokenAgent() => 0x23dB4a08 - gasUsed: 187,019 0.000187019Ξ 0.47 USD @ 1.0 gwei 2500.00 ETH/USD + * accounts[1]->tokenAgentFactory.newTokenAgent() => 0x8EFa1819 - gasUsed: 189,819 0.000189819Ξ 0.47 USD @ 1.0 gwei 2500.00 ETH/USD + * accounts[2]->tokenAgentFactory.newTokenAgent() => 0x6743E5c6 - gasUsed: 189,819 0.000189819Ξ 0.47 USD @ 1.0 gwei 2500.00 ETH/USD + * accounts[3]->tokenAgentFactory.newTokenAgent() => 0xA14d9C7a - gasUsed: 189,819 0.000189819Ξ 0.47 USD @ 1.0 gwei 2500.00 ETH/USD Index Index by Owner tokenAgent Owner ----- -------------- ---------- ---------- 0 0 0x23dB4a08 0xf39Fd6e5 1 0 0x8EFa1819 0x70997970 2 0 0x6743E5c6 0x3C44CdDd 3 0 0xA14d9C7a 0x90F79bf6 - * now: 5:26:36 PM, expiry: 5:28:36 PM - ✔ Test TokenAgent secondary functions (711ms) + * now: 2:38:06 PM, expiry: 2:40:06 PM + ✔ Test TokenAgent secondary functions (726ms) ✔ Test TokenAgent invalid offers # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998936774625231756 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 - 2 0x3C44CdDd 9899.998952196252213041 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.998936759712282965 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 + 2 0x3C44CdDd 9899.998952184273296194 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 - * offers1: [["0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",0,1725866916,0,["0.1","0.2","0.3"],[],["1.0","1.0","0.1"]],["0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",1,1725866916,0,["0.1","0.2","0.3"],[],["1.0","1.0","0.1"]]] - * accounts[1]->tokenAgents[1].addOffers(offers1) => [0, 1] - gasUsed: 471,764 0.000471764Ξ 1.18 USD @ 1.0 gwei 2500.00 ETH/USD - + tokenAgents[1].Offered(index:0, maker: 0x70997970, token: 0xe7f1725E, tokenType: 20, buySell: BUY, expiry: 5:28:36 PM, count: 0, nonce: 0, prices: [0.1,0.2,0.3], tokenIds: [], tokenss: [1,1,0.1], timestamp: 5:28:20 PM) - + tokenAgents[1].Offered(index:1, maker: 0x70997970, token: 0xe7f1725E, tokenType: 20, buySell: SELL, expiry: 5:28:36 PM, count: 0, nonce: 0, prices: [0.1,0.2,0.3], tokenIds: [], tokenss: [1,1,0.1], timestamp: 5:28:20 PM) + * offers1: [["0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",0,1725943206,0,["0.1","0.2","0.3"],[],["1.0","1.0","0.1"]],["0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",1,1725943206,0,["0.1","0.2","0.3"],[],["1.0","1.0","0.1"]]] + * accounts[1]->tokenAgents[1].addOffers(offers1) => [0, 1] - gasUsed: 471,874 0.000471874Ξ 1.18 USD @ 1.0 gwei 2500.00 ETH/USD + + tokenAgents[1].Offered(index:0, maker: 0x70997970, token: 0xe7f1725E, tokenType: 20, buySell: BUY, expiry: 2:40:06 PM, count: 0, nonce: 0, prices: [0.1,0.2,0.3], tokenIds: [], tokenss: [1,1,0.1], timestamp: 2:39:49 PM) + + tokenAgents[1].Offered(index:1, maker: 0x70997970, token: 0xe7f1725E, tokenType: 20, buySell: SELL, expiry: 2:40:06 PM, count: 0, nonce: 0, prices: [0.1,0.2,0.3], tokenIds: [], tokenss: [1,1,0.1], timestamp: 2:39:49 PM) # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998465010091194908 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 - 2 0x3C44CdDd 9899.998952196252213041 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.998464885177649723 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 + 2 0x3C44CdDd 9899.998952184273296194 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 tokenAgents[1] Offers # Token Type B/S Expiry Count Nonce Prices TokenIds Tokenss Useds --- ---------- ---- ---- ------------ ----- ----- ------------------------------ ------------------------------ ------------------------------ ------------------------------ - 0 0xe7f1725E 20 BUY 5:28:36 PM 0 0 0.10, 0.20, 0.30 1.00, 1.00, 0.10 0.00, 0.00, 0.00 - 1 0xe7f1725E 20 SELL 5:28:36 PM 0 0 0.10, 0.20, 0.30 1.00, 1.00, 0.10 0.00, 0.00, 0.00 + 0 0xe7f1725E 20 BUY 2:40:06 PM 0 0 0.10, 0.20, 0.30 1.00, 1.00, 0.10 0.00, 0.00, 0.00 + 1 0xe7f1725E 20 SELL 2:40:06 PM 0 0 0.10, 0.20, 0.30 1.00, 1.00, 0.10 0.00, 0.00, 0.00 * trades1: [[1,"104761904761904761",1,[],["1050000000000000000"]]] - * accounts[2]->tokenAgents[1].trade(trades1, false) - gasUsed: 143,568 0.000143568Ξ 0.36 USD @ 1.0 gwei 2500.00 ETH/USD - + tokenAgents[1].InternalTransfer(from: 0x3C44CdDd, to: 0x8EFa1819, ethers: 10.0, timestamp: 5:28:21 PM) + * accounts[2]->tokenAgents[1].trade(trades1, false) - gasUsed: 143,564 0.000143564Ξ 0.36 USD @ 1.0 gwei 2500.00 ETH/USD + + tokenAgents[1].InternalTransfer(from: 0x3C44CdDd, to: 0x8EFa1819, ethers: 10.0, timestamp: 2:39:50 PM) + erc20Token.Transfer(from: 0x70997970, to: 0x3C44CdDd, tokens: 1.05) - + tokenAgents[1].Traded(index:1, taker: 0x3C44CdDd, maker: 0x70997970, token: 0xe7f1725E, tokenType: 20, makerBuySell: SELL, prices: [0.1,0.2,0], tokenIds: [], tokenss: [1,0.05,0], price: 0.104761904761904761, timestamp: 5:28:21 PM) + + tokenAgents[1].Traded(index:1, taker: 0x3C44CdDd, maker: 0x70997970, token: 0xe7f1725E, tokenType: 20, makerBuySell: SELL, prices: [0.1,0.2,0], tokenIds: [], tokenss: [1,0.05,0], price: 0.104761904761904761, timestamp: 2:39:50 PM) + weth.Transfer(src: 0x3C44CdDd, guy: 0x70997970, wad: 0.11) - + tokenAgents[1].InternalTransfer(from: 0x8EFa1819, to: 0x3C44CdDd, ethers: 10.0, timestamp: 5:28:21 PM) + + tokenAgents[1].InternalTransfer(from: 0x8EFa1819, to: 0x3C44CdDd, ethers: 10.0, timestamp: 2:39:50 PM) # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998465010091194908 100.11 998.95 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 - 2 0x3C44CdDd 9899.998808628109362881 99.89 1001.05 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.998464885177649723 100.11 998.95 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 + 2 0x3C44CdDd 9899.99880862013030645 99.89 1001.05 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 tokenAgents[1] Offers # Token Type B/S Expiry Count Nonce Prices TokenIds Tokenss Useds --- ---------- ---- ---- ------------ ----- ----- ------------------------------ ------------------------------ ------------------------------ ------------------------------ - 0 0xe7f1725E 20 BUY 5:28:36 PM 0 0 0.10, 0.20, 0.30 1.00, 1.00, 0.10 0.00, 0.00, 0.00 - 1 0xe7f1725E 20 SELL 5:28:36 PM 0 0 0.10, 0.20, 0.30 1.00, 1.00, 0.10 1.00, 0.05, 0.00 + 0 0xe7f1725E 20 BUY 2:40:06 PM 0 0 0.10, 0.20, 0.30 1.00, 1.00, 0.10 0.00, 0.00, 0.00 + 1 0xe7f1725E 20 SELL 2:40:06 PM 0 0 0.10, 0.20, 0.30 1.00, 1.00, 0.10 1.00, 0.05, 0.00 - ✔ Test TokenAgent ERC-20 offers and trades (60ms) + ✔ Test TokenAgent ERC-20 offers and trades (57ms) # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998936774625231756 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 - 2 0x3C44CdDd 9899.998952196252213041 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.998936759712282965 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 + 2 0x3C44CdDd 9899.998952184273296194 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 - * offers1: ["0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0,0,1725866916,4,100000000000000000,,","0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0,1,1725866916,4,100000000000000000,4,5,6,7,","0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0,1,1725866916,4,100000000000000000,200000000000000000,300000000000000000,400000000000000000,4,5,6,7,"] - * accounts[1]->tokenAgents[1].addOffers(offers1) => [0, 1, 2] - gasUsed: 447,807 0.000447807Ξ 1.12 USD @ 1.0 gwei 2500.00 ETH/USD - + tokenAgents[1].Offered(index:0, maker: 0x70997970, token: 0x9fE46736, tokenType: 721, buySell: BUY, expiry: 5:28:36 PM, count: 4, nonce: 0, prices: [0.1], tokenIds: [], tokenss: [], timestamp: 5:28:20 PM) - + tokenAgents[1].Offered(index:1, maker: 0x70997970, token: 0x9fE46736, tokenType: 721, buySell: SELL, expiry: 5:28:36 PM, count: 4, nonce: 0, prices: [0.1], tokenIds: [4,5,6,7], tokenss: [], timestamp: 5:28:20 PM) - + tokenAgents[1].Offered(index:2, maker: 0x70997970, token: 0x9fE46736, tokenType: 721, buySell: SELL, expiry: 5:28:36 PM, count: 4, nonce: 0, prices: [0.1,0.2,0.3,0.4], tokenIds: [4,5,6,7], tokenss: [], timestamp: 5:28:20 PM) + * offers1: ["0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0,0,1725943206,4,100000000000000000,,","0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0,1,1725943206,4,100000000000000000,4,5,6,7,","0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0,1,1725943206,4,100000000000000000,200000000000000000,300000000000000000,400000000000000000,4,5,6,7,"] + * accounts[1]->tokenAgents[1].addOffers(offers1) => [0, 1, 2] - gasUsed: 449,305 0.000449305Ξ 1.12 USD @ 1.0 gwei 2500.00 ETH/USD + + tokenAgents[1].Offered(index:0, maker: 0x70997970, token: 0x9fE46736, tokenType: 721, buySell: BUY, expiry: 2:40:06 PM, count: 4, nonce: 0, prices: [0.1], tokenIds: [], tokenss: [], timestamp: 2:39:49 PM) + + tokenAgents[1].Offered(index:1, maker: 0x70997970, token: 0x9fE46736, tokenType: 721, buySell: SELL, expiry: 2:40:06 PM, count: 4, nonce: 0, prices: [0.1], tokenIds: [4,5,6,7], tokenss: [], timestamp: 2:39:49 PM) + + tokenAgents[1].Offered(index:2, maker: 0x70997970, token: 0x9fE46736, tokenType: 721, buySell: SELL, expiry: 2:40:06 PM, count: 4, nonce: 0, prices: [0.1,0.2,0.3,0.4], tokenIds: [4,5,6,7], tokenss: [], timestamp: 2:39:49 PM) # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998488967118314232 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 - 2 0x3C44CdDd 9899.998952196252213041 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.9984874542032204 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 + 2 0x3C44CdDd 9899.998952184273296194 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 tokenAgents[1] Offers # Token Type B/S Expiry Count Nonce Prices TokenIds Tokenss Useds --- ---------- ---- ---- ------------ ----- ----- ------------------------------ ------------------------------ ------------------------------ ------------------------------ - 0 0x9fE46736 721 BUY 5:28:36 PM 4 0 0.10 - 1 0x9fE46736 721 SELL 5:28:36 PM 4 0 0.10 4, 5, 6, 7 - 2 0x9fE46736 721 SELL 5:28:36 PM 4 0 0.10, 0.20, 0.30, 0.40 4, 5, 6, 7 + 0 0x9fE46736 721 BUY 2:40:06 PM 4 0 0.10 + 1 0x9fE46736 721 SELL 2:40:06 PM 4 0 0.10 4, 5, 6, 7 + 2 0x9fE46736 721 SELL 2:40:06 PM 4 0 0.10, 0.20, 0.30, 0.40 4, 5, 6, 7 * trades1: [[1,"400000000000000000",1,[4,5,6,7],[]]] - * accounts[2]->tokenAgents[1].trade(trades1, false) - gasUsed: 166,435 0.000166435Ξ 0.42 USD @ 1.0 gwei 2500.00 ETH/USD - + tokenAgents[1].InternalTransfer(from: 0x3C44CdDd, to: 0x8EFa1819, ethers: 10.0, timestamp: 5:28:21 PM) + * accounts[2]->tokenAgents[1].trade(trades1, false) - gasUsed: 166,428 0.000166428Ξ 0.42 USD @ 1.0 gwei 2500.00 ETH/USD + + tokenAgents[1].InternalTransfer(from: 0x3C44CdDd, to: 0x8EFa1819, ethers: 10.0, timestamp: 2:39:50 PM) + erc721Token.Approval(from: 0x70997970, to: 0x00000000, tokenId: 4) + erc721Token.Transfer(from: 0x70997970, to: 0x3C44CdDd, tokenId: 4) + erc721Token.Approval(from: 0x70997970, to: 0x00000000, tokenId: 5) @@ -103,80 +103,80 @@ + erc721Token.Transfer(from: 0x70997970, to: 0x3C44CdDd, tokenId: 6) + erc721Token.Approval(from: 0x70997970, to: 0x00000000, tokenId: 7) + erc721Token.Transfer(from: 0x70997970, to: 0x3C44CdDd, tokenId: 7) - + tokenAgents[1].Traded(index:1, taker: 0x3C44CdDd, maker: 0x70997970, token: 0x9fE46736, tokenType: 721, makerBuySell: SELL, prices: [0.1,0.1,0.1,0.1], tokenIds: [4,5,6,7], tokenss: [], price: 0.4, timestamp: 5:28:21 PM) + + tokenAgents[1].Traded(index:1, taker: 0x3C44CdDd, maker: 0x70997970, token: 0x9fE46736, tokenType: 721, makerBuySell: SELL, prices: [0.1,0.1,0.1,0.1], tokenIds: [4,5,6,7], tokenss: [], price: 0.4, timestamp: 2:39:50 PM) + weth.Transfer(src: 0x3C44CdDd, guy: 0x70997970, wad: 0.4) - + tokenAgents[1].InternalTransfer(from: 0x8EFa1819, to: 0x3C44CdDd, ethers: 10.0, timestamp: 5:28:21 PM) + + tokenAgents[1].InternalTransfer(from: 0x8EFa1819, to: 0x3C44CdDd, ethers: 10.0, timestamp: 2:39:50 PM) # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998488967118314232 100.4 1000.0 0:20, 1:20, 2:20, 3:20 - 2 0x3C44CdDd 9899.998785761086610216 99.6 1000.0 4, 5, 6, 7, 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.9984874542032204 100.4 1000.0 0:20, 1:20, 2:20, 3:20 + 2 0x3C44CdDd 9899.998785756107533906 99.6 1000.0 4, 5, 6, 7, 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 tokenAgents[1] Offers # Token Type B/S Expiry Count Nonce Prices TokenIds Tokenss Useds --- ---------- ---- ---- ------------ ----- ----- ------------------------------ ------------------------------ ------------------------------ ------------------------------ - 0 0x9fE46736 721 BUY 5:28:36 PM 4 0 0.10 - 1 0x9fE46736 721 SELL 5:28:36 PM 0 0 0.10 4, 5, 6, 7 - 2 0x9fE46736 721 SELL 5:28:36 PM 4 0 0.10, 0.20, 0.30, 0.40 4, 5, 6, 7 + 0 0x9fE46736 721 BUY 2:40:06 PM 4 0 0.10 + 1 0x9fE46736 721 SELL 2:40:06 PM 0 0 0.10 4, 5, 6, 7 + 2 0x9fE46736 721 SELL 2:40:06 PM 4 0 0.10, 0.20, 0.30, 0.40 4, 5, 6, 7 - ✔ Test TokenAgent ERC-721 offers and trades (58ms) + ✔ Test TokenAgent ERC-721 offers and trades (55ms) # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998936774625231756 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 - 2 0x3C44CdDd 9899.998952196252213041 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.998936759712282965 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 + 2 0x3C44CdDd 9899.998952184273296194 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 - * offers1: ["0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9,0,1725866916,40,100000000000000000,,","0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9,1,1725866916,26,100000000000000000,0,1,2,3,5,6,7,8","0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9,1,1725866916,40,100000000000000000,200000000000000000,300000000000000000,400000000000000000,0,1,2,3,10,10,10,10"] - * accounts[1]->tokenAgents[1].addOffers(offers1) => [0, 1, 2] - gasUsed: 638,952 0.000638952Ξ 1.60 USD @ 1.0 gwei 2500.00 ETH/USD - + tokenAgents[1].Offered(index:0, maker: 0x70997970, token: 0xCf7Ed3Ac, tokenType: 1155, buySell: BUY, expiry: 5:28:36 PM, count: 40, nonce: 0, prices: [0.1], tokenIds: [], tokenss: [], timestamp: 5:28:20 PM) - + tokenAgents[1].Offered(index:1, maker: 0x70997970, token: 0xCf7Ed3Ac, tokenType: 1155, buySell: SELL, expiry: 5:28:36 PM, count: 26, nonce: 0, prices: [0.1], tokenIds: [0,1,2,3], tokenss: [5,6,7,8], timestamp: 5:28:20 PM) - + tokenAgents[1].Offered(index:2, maker: 0x70997970, token: 0xCf7Ed3Ac, tokenType: 1155, buySell: SELL, expiry: 5:28:36 PM, count: 40, nonce: 0, prices: [0.1,0.2,0.3,0.4], tokenIds: [0,1,2,3], tokenss: [10,10,10,10], timestamp: 5:28:20 PM) + * offers1: ["0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9,0,1725943206,40,100000000000000000,,","0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9,1,1725943206,26,100000000000000000,0,1,2,3,5,6,7,8","0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9,1,1725943206,40,100000000000000000,200000000000000000,300000000000000000,400000000000000000,0,1,2,3,10,10,10,10"] + * accounts[1]->tokenAgents[1].addOffers(offers1) => [0, 1, 2] - gasUsed: 643,065 0.000643065Ξ 1.61 USD @ 1.0 gwei 2500.00 ETH/USD + + tokenAgents[1].Offered(index:0, maker: 0x70997970, token: 0xCf7Ed3Ac, tokenType: 1155, buySell: BUY, expiry: 2:40:06 PM, count: 40, nonce: 0, prices: [0.1], tokenIds: [], tokenss: [], timestamp: 2:39:49 PM) + + tokenAgents[1].Offered(index:1, maker: 0x70997970, token: 0xCf7Ed3Ac, tokenType: 1155, buySell: SELL, expiry: 2:40:06 PM, count: 26, nonce: 0, prices: [0.1], tokenIds: [0,1,2,3], tokenss: [5,6,7,8], timestamp: 2:39:49 PM) + + tokenAgents[1].Offered(index:2, maker: 0x70997970, token: 0xCf7Ed3Ac, tokenType: 1155, buySell: SELL, expiry: 2:40:06 PM, count: 40, nonce: 0, prices: [0.1,0.2,0.3,0.4], tokenIds: [0,1,2,3], tokenss: [10,10,10,10], timestamp: 2:39:49 PM) # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998297821901938092 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 - 2 0x3C44CdDd 9899.998952196252213041 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.99829369398369032 100.0 1000.0 4, 5, 6, 7 0:20, 1:20, 2:20, 3:20 + 2 0x3C44CdDd 9899.998952184273296194 100.0 1000.0 8, 9, 10, 11 0:30, 1:30, 2:30, 3:30 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 tokenAgents[1] Offers # Token Type B/S Expiry Count Nonce Prices TokenIds Tokenss Useds --- ---------- ---- ---- ------------ ----- ----- ------------------------------ ------------------------------ ------------------------------ ------------------------------ - 0 0xCf7Ed3Ac 1155 BUY 5:28:36 PM 40 0 0.10 - 1 0xCf7Ed3Ac 1155 SELL 5:28:36 PM 26 0 0.10 0, 1, 2, 3 5, 6, 7, 8 0, 0, 0, 0 - 2 0xCf7Ed3Ac 1155 SELL 5:28:36 PM 40 0 0.10, 0.20, 0.30, 0.40 0, 1, 2, 3 10, 10, 10, 10 0, 0, 0, 0 + 0 0xCf7Ed3Ac 1155 BUY 2:40:06 PM 40 0 0.10 + 1 0xCf7Ed3Ac 1155 SELL 2:40:06 PM 26 0 0.10 0, 1, 2, 3 5, 6, 7, 8 0, 0, 0, 0 + 2 0xCf7Ed3Ac 1155 SELL 2:40:06 PM 40 0 0.10, 0.20, 0.30, 0.40 0, 1, 2, 3 10, 10, 10, 10 0, 0, 0, 0 * trades1: [[1,"2600000000000000000",1,[0,1,2,3],[5,6,7,8]]] - * accounts[2]->tokenAgents[1].trade(trades1, false) - gasUsed: 172,355 0.000172355Ξ 0.43 USD @ 1.0 gwei 2500.00 ETH/USD - + tokenAgents[1].InternalTransfer(from: 0x3C44CdDd, to: 0x8EFa1819, ethers: 10.0, timestamp: 5:28:21 PM) + * accounts[2]->tokenAgents[1].trade(trades1, false) - gasUsed: 172,348 0.000172348Ξ 0.43 USD @ 1.0 gwei 2500.00 ETH/USD + + tokenAgents[1].InternalTransfer(from: 0x3C44CdDd, to: 0x8EFa1819, ethers: 10.0, timestamp: 2:39:50 PM) + erc1155Token.TransferSingle(operator: 0x8EFa1819, from: 0x70997970, to: 0x3C44CdDd, tokenId: 0, tokens: 5) + erc1155Token.TransferSingle(operator: 0x8EFa1819, from: 0x70997970, to: 0x3C44CdDd, tokenId: 1, tokens: 6) + erc1155Token.TransferSingle(operator: 0x8EFa1819, from: 0x70997970, to: 0x3C44CdDd, tokenId: 2, tokens: 7) + erc1155Token.TransferSingle(operator: 0x8EFa1819, from: 0x70997970, to: 0x3C44CdDd, tokenId: 3, tokens: 8) - + tokenAgents[1].Traded(index:1, taker: 0x3C44CdDd, maker: 0x70997970, token: 0xCf7Ed3Ac, tokenType: 1155, makerBuySell: SELL, prices: [0.1,0.1,0.1,0.1], tokenIds: [0,1,2,3], tokenss: [5,6,7,8], price: 2.6, timestamp: 5:28:21 PM) + + tokenAgents[1].Traded(index:1, taker: 0x3C44CdDd, maker: 0x70997970, token: 0xCf7Ed3Ac, tokenType: 1155, makerBuySell: SELL, prices: [0.1,0.1,0.1,0.1], tokenIds: [0,1,2,3], tokenss: [5,6,7,8], price: 2.6, timestamp: 2:39:50 PM) + weth.Transfer(src: 0x3C44CdDd, guy: 0x70997970, wad: 2.6) - + tokenAgents[1].InternalTransfer(from: 0x8EFa1819, to: 0x3C44CdDd, ethers: 10.0, timestamp: 5:28:21 PM) + + tokenAgents[1].InternalTransfer(from: 0x8EFa1819, to: 0x3C44CdDd, ethers: 10.0, timestamp: 2:39:50 PM) # Account ETH WETH 0x5FbDB231 ERC-20 0xe7f1725E ERC-721 0x9fE46736 ERC-1155 0xCf7Ed3Ac --- ---------- ------------------------ ------------------------ ------------------------ ---------------------------------- ---------------------------------- - 0 0xf39Fd6e5 9899.973692496174290761 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 - 1 0x70997970 9899.998297821901938092 102.6 1000.0 4, 5, 6, 7 0:15, 1:14, 2:13, 3:12 - 2 0x3C44CdDd 9899.998779841080375106 97.4 1000.0 8, 9, 10, 11 0:35, 1:36, 2:37, 3:38 - 3 0x90F79bf6 9899.998963934171431042 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 + 0 0xf39Fd6e5 9899.973659580492531542 100.0 1000.0 0, 1, 2, 3 0:10, 1:10, 2:10, 3:10 + 1 0x70997970 9899.99829369398369032 102.6 1000.0 4, 5, 6, 7 0:15, 1:14, 2:13, 3:12 + 2 0x3C44CdDd 9899.99877983610129289 97.4 1000.0 8, 9, 10, 11 0:35, 1:36, 2:37, 3:38 + 3 0x90F79bf6 9899.998963924415837957 100.0 1000.0 12, 13, 14, 15 0:40, 1:40, 2:40, 3:40 tokenAgents[1] Offers # Token Type B/S Expiry Count Nonce Prices TokenIds Tokenss Useds --- ---------- ---- ---- ------------ ----- ----- ------------------------------ ------------------------------ ------------------------------ ------------------------------ - 0 0xCf7Ed3Ac 1155 BUY 5:28:36 PM 40 0 0.10 - 1 0xCf7Ed3Ac 1155 SELL 5:28:36 PM 0 0 0.10 0, 1, 2, 3 5, 6, 7, 8 0, 0, 0, 0 - 2 0xCf7Ed3Ac 1155 SELL 5:28:36 PM 40 0 0.10, 0.20, 0.30, 0.40 0, 1, 2, 3 10, 10, 10, 10 0, 0, 0, 0 + 0 0xCf7Ed3Ac 1155 BUY 2:40:06 PM 40 0 0.10 + 1 0xCf7Ed3Ac 1155 SELL 2:40:06 PM 0 0 0.10 0, 1, 2, 3 5, 6, 7, 8 0, 0, 0, 0 + 2 0xCf7Ed3Ac 1155 SELL 2:40:06 PM 40 0 0.10, 0.20, 0.30, 0.40 0, 1, 2, 3 10, 10, 10, 10 0, 0, 0, 0 - ✔ Test TokenAgent ERC-1155 offers and trades (57ms) + ✔ Test TokenAgent ERC-1155 offers and trades (55ms) - 5 passing (903ms) + 5 passing (909ms)