diff --git a/.gitmodules b/.gitmodules index 06a43b1656..b78d173c56 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/rain.interpreter"] path = lib/rain.interpreter url = https://github.com/rainlanguage/rain.interpreter +[submodule "lib/rain.tofu.erc20-decimals"] + path = lib/rain.tofu.erc20-decimals + url = https://github.com/rainlanguage/rain.tofu.erc20-decimals diff --git a/REUSE.toml b/REUSE.toml index 2312176b63..d27017ee3c 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -15,6 +15,7 @@ path = [ "flake.lock", "flake.nix", "foundry.toml", + "foundry.lock", "slither.config.json", "typeshare.toml", "REUSE.toml", diff --git a/crates/cli/src/commands/order/calldata.rs b/crates/cli/src/commands/order/calldata.rs index eacb0942d5..0900b0e25e 100644 --- a/crates/cli/src/commands/order/calldata.rs +++ b/crates/cli/src/commands/order/calldata.rs @@ -133,25 +133,25 @@ mod tests { }) }; - // mock iInterpreter() call + // mock I_INTERPRETER() call rpc_server.mock(|when, then| { - when.path("/rpc").body_contains("0xf0cfdd37"); + when.path("/rpc").body_contains("0x56fb83e9"); then.status(200) .header("content-type", "application/json") .json_body(build_address_return(1)); }); - // mock iStore() call + // mock I_STORE() call rpc_server.mock(|when, then| { - when.path("/rpc").body_contains("0xc19423bc"); + when.path("/rpc").body_contains("0x251ac32e"); then.status(200) .header("content-type", "application/json") .json_body(build_address_return(2)); }); - // mock iParser() call + // mock I_PARSER() call rpc_server.mock(|when, then| { - when.path("/rpc").body_contains("0x24376855"); + when.path("/rpc").body_contains("0xf79693f4"); then.status(200) .header("content-type", "application/json") .json_body(build_address_return(3)); diff --git a/crates/common/src/fuzz/impls.rs b/crates/common/src/fuzz/impls.rs index 643bdf2ca6..0d49e2eebf 100644 --- a/crates/common/src/fuzz/impls.rs +++ b/crates/common/src/fuzz/impls.rs @@ -14,7 +14,7 @@ use rain_error_decoding::{AbiDecodeFailedErrors, AbiDecodedErrorType}; use rain_interpreter_bindings::IInterpreterStoreV3::FullyQualifiedNamespace; use rain_interpreter_bindings::IInterpreterV4::EvalV4; use rain_interpreter_bindings::{ - DeployerISP::{iInterpreterCall, iStoreCall}, + DeployerISP::{I_INTERPRETERCall, I_STORECall}, IInterpreterV4::eval4Call, }; use rain_interpreter_eval::eval::ForkParseArgs; @@ -496,7 +496,7 @@ impl FuzzRunner { .map_err(|e| FuzzRunnerError::ForkCallError(Box::new(e)))?; let store = self .forker - .alloy_call(Address::default(), deployer.address, iStoreCall {}, true) + .alloy_call(Address::default(), deployer.address, I_STORECall {}, true) .await? .typed_return; @@ -505,7 +505,7 @@ impl FuzzRunner { .alloy_call( Address::default(), deployer.address, - iInterpreterCall {}, + I_INTERPRETERCall {}, true, ) .await? diff --git a/crates/common/src/types/order_takes_list_flattened.rs b/crates/common/src/types/order_takes_list_flattened.rs index 2e6ab8c3c0..77e9f3f65c 100644 --- a/crates/common/src/types/order_takes_list_flattened.rs +++ b/crates/common/src/types/order_takes_list_flattened.rs @@ -297,7 +297,7 @@ mod tests { let input_display = "-1.234567890123456789".to_string(); let input_amount = Float::parse(input_display.clone()).unwrap(); - let output_display = "-0.98765432".to_string(); + let output_display = "-9.8765432e-1".to_string(); let output_amount = Float::parse(output_display.clone()).unwrap(); trade_data.input_vault_balance_change.amount = SgBytes(input_amount.as_hex()); diff --git a/crates/common/src/types/token_vault_flattened.rs b/crates/common/src/types/token_vault_flattened.rs index 39b696db28..d6399fb36b 100644 --- a/crates/common/src/types/token_vault_flattened.rs +++ b/crates/common/src/types/token_vault_flattened.rs @@ -226,7 +226,7 @@ mod tests { let result = TokenVaultFlattened::try_from(sg_vault); let flattened = result.unwrap(); - assert_eq!(flattened.balance_display, "9.87650000000000000000001e4"); + assert_eq!(flattened.balance_display, "98765.0000000000000000001"); assert_eq!(flattened.balance, SgBytes(balance_str.to_string())); } } diff --git a/crates/common/src/types/vault_balance_change_flattened.rs b/crates/common/src/types/vault_balance_change_flattened.rs index 9b477121e0..80669c7744 100644 --- a/crates/common/src/types/vault_balance_change_flattened.rs +++ b/crates/common/src/types/vault_balance_change_flattened.rs @@ -156,7 +156,7 @@ mod tests { let result = VaultBalanceChangeFlattened::try_from(val); assert!(result.is_ok()); let flattened = result.unwrap(); - assert_eq!(flattened.amount_display_signed, "-0.5"); + assert_eq!(flattened.amount_display_signed, "-5e-1"); } #[test] diff --git a/crates/quote/src/rpc.rs b/crates/quote/src/rpc.rs index 64fbe61dc9..c15ce5c42b 100644 --- a/crates/quote/src/rpc.rs +++ b/crates/quote/src/rpc.rs @@ -256,7 +256,7 @@ mod tests { }, MulticallResult { success: false, - returnData: alloy::hex!("deadbeef").to_vec().into(), // Unknown error selector + returnData: alloy::hex!("ff00ff00").to_vec().into(), // Unknown error selector }, ] .abi_encode(); diff --git a/crates/test_fixtures/src/lib.rs b/crates/test_fixtures/src/lib.rs index d8c12a8f55..2229f89692 100644 --- a/crates/test_fixtures/src/lib.rs +++ b/crates/test_fixtures/src/lib.rs @@ -3,7 +3,7 @@ use alloy::{ hex::decode, network::{AnyNetwork, AnyTransactionReceipt, EthereumWallet, TransactionBuilder}, node_bindings::{Anvil, AnvilInstance}, - primitives::{utils::parse_units, Address, Bytes, B256, U256}, + primitives::{address, utils::parse_units, Address, Bytes, B256, U256}, providers::{ext::AnvilApi, PendingTransactionError, Provider, ProviderBuilder}, rpc::types::TransactionRequest, serde::WithOtherFields, @@ -14,7 +14,7 @@ use alloy::{ }; pub use rain_interpreter_test_fixtures::{Deployer, Interpreter, Parser, Store, ERC20}; use serde_json::value::RawValue; -use std::{marker::PhantomData, str::FromStr}; +use std::marker::PhantomData; use rain_interpreter_test_fixtures::LocalEvmProvider; use rain_math_float::Float; @@ -34,6 +34,11 @@ sol!( "../../lib/rain.interpreter/lib/rain.interpreter.interface/lib/forge-std/src/interfaces//IMulticall3.sol" ); +sol!( + #![sol(all_derives = true, rpc = true)] + TOFUTokenDecimals, "../../lib/rain.tofu.erc20-decimals/out/TOFUTokenDecimals.sol/TOFUTokenDecimals.json" +); + /// A local evm instance that wraps an Anvil instance and provider with /// its signers, and with rain contracts already deployed on it. /// The first signer wallet is the main wallet that would sign any transactions @@ -104,6 +109,14 @@ impl LocalEvm { .wallet(signer_wallets[0].clone()) .connect_http(anvil.endpoint_url()); + provider + .anvil_set_code( + TOFU_TOKEN_DECIMALS_ADDRESS, + TOFUTokenDecimals::DEPLOYED_BYTECODE.clone(), + ) + .await + .unwrap(); + // deploy rain contracts let orderbook = Orderbook::deploy(provider.clone()).await.unwrap(); let orderbook_subparser = OrderbookSubParser::deploy(provider.clone()).await.unwrap(); @@ -118,15 +131,14 @@ impl LocalEvm { let deployer = Deployer::deploy(provider.clone(), config).await.unwrap(); // set the multicall 3 contract at its official address - let multicall3_address = Address::from_str(MULTICALL3_ADDRESS).unwrap(); provider .anvil_set_code( - multicall3_address, + MULTICALL3_ADDRESS, decode(MULTICALL3_BYTECODE).unwrap().into(), ) .await .unwrap(); - let multicall3 = IMulticall3::new(multicall3_address, provider.clone()); + let multicall3 = IMulticall3::new(MULTICALL3_ADDRESS, provider.clone()); Self { anvil, @@ -336,5 +348,7 @@ impl LocalEvm { } } -const MULTICALL3_ADDRESS: &str = "0xcA11bde05977b3631167028862bE2a173976CA11"; +const MULTICALL3_ADDRESS: Address = address!("cA11bde05977b3631167028862bE2a173976CA11"); const MULTICALL3_BYTECODE: &str = "0x6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c0033"; + +const TOFU_TOKEN_DECIMALS_ADDRESS: Address = address!("4f1C29FAAB7EDdF8D7794695d8259996734Cc665"); diff --git a/flake.lock b/flake.lock index 588c043098..fa9aa44c33 100644 --- a/flake.lock +++ b/flake.lock @@ -182,11 +182,11 @@ "nixpkgs": "nixpkgs_5" }, "locked": { - "lastModified": 1741023058, - "narHash": "sha256-LSd/8CBlpDLjci5ANFJjP0w+dGdY/mqKsyUfhjGwnfs=", + "lastModified": 1758705030, + "narHash": "sha256-zYM8PiEXANNrtjfyGUc7w37/D/kCynp0cQS+wCQ77GI=", "owner": "shazow", "repo": "foundry.nix", - "rev": "66becfe20b7e688b8f2e5774609c4436cf202ba0", + "rev": "b59a55014050110170023e3e1c277c1d4a2f055b", "type": "github" }, "original": { @@ -209,6 +209,22 @@ "type": "indirect" } }, + "nixpkgs-old": { + "locked": { + "lastModified": 1749104371, + "narHash": "sha256-m2NmOPd6XgBiskmUq/BS9Xxuf3z0ebnGVfSKNAO5NEM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "48975d7f9b9960ed33c4e8561bcce20cc0c2de5b", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "48975d7f9b9960ed33c4e8561bcce20cc0c2de5b", + "type": "github" + } + }, "nixpkgs_2": { "locked": { "lastModified": 1714764285, @@ -272,11 +288,11 @@ }, "nixpkgs_6": { "locked": { - "lastModified": 1749104371, - "narHash": "sha256-m2NmOPd6XgBiskmUq/BS9Xxuf3z0ebnGVfSKNAO5NEM=", + "lastModified": 1758711836, + "narHash": "sha256-uBqPg7wNX2v6YUdTswH7wWU8wqb60cFZx0tHaWTGF30=", "owner": "nixos", "repo": "nixpkgs", - "rev": "48975d7f9b9960ed33c4e8561bcce20cc0c2de5b", + "rev": "46f97b78e825ae762c0224e3983c47687436a498", "type": "github" }, "original": { @@ -363,15 +379,16 @@ "flake-utils": "flake-utils_7", "foundry": "foundry_2", "nixpkgs": "nixpkgs_6", + "nixpkgs-old": "nixpkgs-old", "rust-overlay": "rust-overlay_2", "solc": "solc_2" }, "locked": { - "lastModified": 1757439424, - "narHash": "sha256-slPazOVyDSOaG7uL/E4Er2gbS5Zx7037oYHrSE2GuO4=", + "lastModified": 1761735588, + "narHash": "sha256-414jL0IpGHzOztZIvd/ePiPakus+OFZ51A1xlEVHhN4=", "owner": "rainlanguage", "repo": "rainix", - "rev": "7f734b1da77771b5d6ee5449f43c2a1a43705149", + "rev": "8354e4956ed60f33135598750e4442f075a23c10", "type": "github" }, "original": { @@ -411,11 +428,11 @@ "nixpkgs": "nixpkgs_7" }, "locked": { - "lastModified": 1749091064, - "narHash": "sha256-TGtYjzRX0sueFhwYsnNNFF5TTKnpnloznpIghLzxeXo=", + "lastModified": 1758681214, + "narHash": "sha256-8cW731vev6kfr58cILO2ZsjHwaPhm88dQ8Q6nTSjP9I=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "12419593ce78f2e8e1e89a373c6515885e218acb", + "rev": "b12ed88d8d33d4f3cbc842bf29fad93bb1437299", "type": "github" }, "original": { @@ -446,13 +463,13 @@ "solc-macos-amd64-list-json": { "flake": false, "locked": { - "narHash": "sha256-U5ckttxwKO13gIKggel6iybG5oTDbSidPR5nH3Gs+kY=", + "narHash": "sha256-AvITkfpNYgCypXuLJyqco0li+unVw39BAfdOZvd/SPE=", "type": "file", - "url": "https://github.com/ethereum/solc-bin/raw/30a3695/macosx-amd64/list.json" + "url": "https://github.com/argotorg/solc-bin/raw/26fc3fd/macosx-amd64/list.json" }, "original": { "type": "file", - "url": "https://github.com/ethereum/solc-bin/raw/30a3695/macosx-amd64/list.json" + "url": "https://github.com/argotorg/solc-bin/raw/26fc3fd/macosx-amd64/list.json" } }, "solc_2": { @@ -462,11 +479,11 @@ "solc-macos-amd64-list-json": "solc-macos-amd64-list-json" }, "locked": { - "lastModified": 1748780655, - "narHash": "sha256-mradCdMvjXwKd7kVFACB/d1CP2LLCyEgUu4vJCSzNLU=", + "lastModified": 1756368702, + "narHash": "sha256-cqEHv7uCV0LibmQphyiXZ1+jYtGjMNb9Pae4tfcAcF8=", "owner": "hellwolf", "repo": "solc.nix", - "rev": "3b6f3223ace5a7bc400b01a434d86bb1cb2593fb", + "rev": "d83e90df2fa8359a690f6baabf76099432193c3f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 0db84d01db..ef1fa50678 100644 --- a/flake.nix +++ b/flake.nix @@ -9,7 +9,9 @@ outputs = { self, flake-utils, rainix, rain }: flake-utils.lib.eachDefaultSystem (system: - let pkgs = rainix.pkgs.${system}; + let + pkgs = rainix.pkgs.${system}; + old-pkgs = rainix.old-pkgs.${system}; in rec { packages = rec { @@ -133,7 +135,7 @@ echo COMMIT_SHA=''${COMMIT_SHA} >> .env echo VITE_WALLETCONNECT_PROJECT_ID=''${VITE_WALLETCONNECT_PROJECT_ID} >> .env ''; - additionalBuildInputs = [ pkgs.sentry-cli ]; + additionalBuildInputs = [ old-pkgs.sentry-cli ]; }; ob-tauri-before-build-ci = rainix.mkTask.${system} { diff --git a/foundry.lock b/foundry.lock new file mode 100644 index 0000000000..55989f8d99 --- /dev/null +++ b/foundry.lock @@ -0,0 +1,14 @@ +{ + "lib/rain.interpreter": { + "rev": "d1fee1b9c4a427ebd529b9c0b0e17718c1a4a99a" + }, + "lib/rain.orderbook.interface": { + "rev": "dae86d795b32cb88005d9c47cbbbff9a5eef940a" + }, + "lib/rain.tofu.erc20-decimals": { + "rev": "ca8e8bacf6c6cb3b9938a6961efb9f93cc1754f9" + }, + "lib/sushixswap-v2": { + "rev": "ca38c64cf7c3042f6427414d7bff61c54293da51" + } +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 3b03b09eaf..869fc60ad2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -45,6 +45,7 @@ remappings = [ "rain.sol.codegen/=lib/rain.interpreter/lib/rain.interpreter.interface/lib/rain.sol.codegen/src/", "rain.solmem/=lib/rain.orderbook.interface/lib/rain.interpreter.interface/lib/rain.solmem/src", "rain.math.float/=lib/rain.orderbook.interface/lib/rain.interpreter.interface/lib/rain.math.float/src/", + "rain.tofu.erc20-decimals/=lib/rain.tofu.erc20-decimals/src/", ] [fuzz] diff --git a/lib/rain.interpreter b/lib/rain.interpreter index 98f76e26bf..d1fee1b9c4 160000 --- a/lib/rain.interpreter +++ b/lib/rain.interpreter @@ -1 +1 @@ -Subproject commit 98f76e26bf47e9062ba56c900acb700f912a470e +Subproject commit d1fee1b9c4a427ebd529b9c0b0e17718c1a4a99a diff --git a/lib/rain.orderbook.interface b/lib/rain.orderbook.interface index 698588adcf..dae86d795b 160000 --- a/lib/rain.orderbook.interface +++ b/lib/rain.orderbook.interface @@ -1 +1 @@ -Subproject commit 698588adcf35117864fe56fef8b83f436b6db2b1 +Subproject commit dae86d795b32cb88005d9c47cbbbff9a5eef940a diff --git a/lib/rain.tofu.erc20-decimals b/lib/rain.tofu.erc20-decimals new file mode 160000 index 0000000000..ca8e8bacf6 --- /dev/null +++ b/lib/rain.tofu.erc20-decimals @@ -0,0 +1 @@ +Subproject commit ca8e8bacf6c6cb3b9938a6961efb9f93cc1754f9 diff --git a/packages/orderbook/test/js_api/gui.test.ts b/packages/orderbook/test/js_api/gui.test.ts index 3b16075ba2..3a34ba875b 100644 --- a/packages/orderbook/test/js_api/gui.test.ts +++ b/packages/orderbook/test/js_api/gui.test.ts @@ -1232,17 +1232,17 @@ ${dotrain}`; it('generates deposit calldatas', async () => { await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xf0cfdd37') + .withBodyIncluding('0x56fb83e9') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '1'.repeat(40)}`); - // iStore() call + // I_STORE()() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xc19423bc') + .withBodyIncluding('0x251ac32e') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '2'.repeat(40)}`); - // iParser() call + // I_PARSER() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0x24376855') + .withBodyIncluding('0xf79693f4') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '3'.repeat(40)}`); // parse2() call await mockServer @@ -1280,17 +1280,17 @@ ${dotrain}`; it('generates add order calldata', async () => { await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xf0cfdd37') + .withBodyIncluding('0x56fb83e9') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '1'.repeat(40)}`); - // iStore() call + // I_STORE()() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xc19423bc') + .withBodyIncluding('0x251ac32e') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '2'.repeat(40)}`); - // iParser() call + // I_PARSER() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0x24376855') + .withBodyIncluding('0xf79693f4') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '3'.repeat(40)}`); // parse2() call await mockServer @@ -1317,17 +1317,17 @@ ${dotrain}`; it('generates add order calldata without entering field value', async () => { await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xf0cfdd37') + .withBodyIncluding('0x56fb83e9') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '1'.repeat(40)}`); - // iStore() call + // I_STORE()() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xc19423bc') + .withBodyIncluding('0x251ac32e') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '2'.repeat(40)}`); - // iParser() call + // I_PARSER() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0x24376855') + .withBodyIncluding('0xf79693f4') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '3'.repeat(40)}`); // parse2() call await mockServer @@ -1352,17 +1352,17 @@ ${dotrain}`; it('should generate multicalldata for deposit and add order with existing vault ids', async () => { await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xf0cfdd37') + .withBodyIncluding('0x56fb83e9') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '1'.repeat(40)}`); - // iStore() call + // I_STORE()() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xc19423bc') + .withBodyIncluding('0x251ac32e') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '2'.repeat(40)}`); - // iParser() call + // I_PARSER() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0x24376855') + .withBodyIncluding('0xf79693f4') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '3'.repeat(40)}`); // parse2() call await mockServer @@ -1394,17 +1394,17 @@ ${dotrain}`; it('should generate multicalldata for deposit and add order with missing field value', async () => { await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xf0cfdd37') + .withBodyIncluding('0x56fb83e9') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '1'.repeat(40)}`); - // iStore() call + // I_STORE()() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xc19423bc') + .withBodyIncluding('0x251ac32e') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '2'.repeat(40)}`); - // iParser() call + // I_PARSER() call await mockServer .forPost('/rpc-url') - .withBodyIncluding('0x24376855') + .withBodyIncluding('0xf79693f4') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '3'.repeat(40)}`); // parse2() call await mockServer @@ -1458,15 +1458,15 @@ ${dotrainWithoutVaultIds}`; await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xf0cfdd37') + .withBodyIncluding('0x56fb83e9') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '1'.repeat(40)}`); await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xc19423bc') + .withBodyIncluding('0x251ac32e') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '2'.repeat(40)}`); await mockServer .forPost('/rpc-url') - .withBodyIncluding('0x24376855') + .withBodyIncluding('0xf79693f4') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '3'.repeat(40)}`); await mockServer .forPost('/rpc-url') @@ -1742,15 +1742,15 @@ ${dotrainWithoutVaultIds}`; ); await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xf0cfdd37') + .withBodyIncluding('0x56fb83e9') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '1'.repeat(40)}`); await mockServer .forPost('/rpc-url') - .withBodyIncluding('0xc19423bc') + .withBodyIncluding('0x251ac32e') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '2'.repeat(40)}`); await mockServer .forPost('/rpc-url') - .withBodyIncluding('0x24376855') + .withBodyIncluding('0xf79693f4') .thenSendJsonRpcResult(`0x${'0'.repeat(24) + '3'.repeat(40)}`); await mockServer .forPost('/rpc-url') @@ -2048,7 +2048,7 @@ ${dotrainWithoutVaultIds}`; ) ); assert.equal(result.balance.toFixedDecimal(18).value, BigInt(1000)); - assert.equal(result.formattedBalance, '0.000000000000001'); + assert.equal(result.formattedBalance, '1e-15'); }); }); diff --git a/packages/orderbook/test/js_api/raindexClient.test.ts b/packages/orderbook/test/js_api/raindexClient.test.ts index 4887a7d4e7..a8349f4e63 100644 --- a/packages/orderbook/test/js_api/raindexClient.test.ts +++ b/packages/orderbook/test/js_api/raindexClient.test.ts @@ -1951,7 +1951,7 @@ describe('Rain Orderbook JS API Package Bindgen Tests - Raindex Client', async f const res = extractWasmEncodedData(await vault.getOwnerBalance()); assert.equal(res.balance.toFixedDecimal(18).value, BigInt(1000)); - assert.equal(res.formattedBalance, '0.000000000000001'); + assert.equal(res.formattedBalance, '1e-15'); }); }); diff --git a/packages/webapp/src/lib/constants.ts b/packages/webapp/src/lib/constants.ts index cb8bb2718d..b5aa1122db 100644 --- a/packages/webapp/src/lib/constants.ts +++ b/packages/webapp/src/lib/constants.ts @@ -1,4 +1,4 @@ export const REGISTRY_URL = - 'https://raw.githubusercontent.com/rainlanguage/rain.strategies/e5fb0899864c9a2b084dd97312f78ccac1444cab/registry'; + 'https://raw.githubusercontent.com/rainlanguage/rain.strategies/d57c3aa77b63f90535957947d15146c0182367dc/registry'; export const REMOTE_SETTINGS_URL = - 'https://raw.githubusercontent.com/rainlanguage/rain.strategies/e5fb0899864c9a2b084dd97312f78ccac1444cab/settings.yaml'; + 'https://raw.githubusercontent.com/rainlanguage/rain.strategies/d57c3aa77b63f90535957947d15146c0182367dc/settings.yaml'; diff --git a/pointers.sh b/pointers.sh index 0211dded96..9d18e673b8 100755 --- a/pointers.sh +++ b/pointers.sh @@ -9,6 +9,7 @@ set -euxo pipefail (cd lib/rain.interpreter && nix develop -c i9r-prelude) (cd lib/rain.interpreter/lib/rain.metadata && nix develop -c rainix-sol-prelude) (cd lib/rain.interpreter/lib/rain.metadata && nix develop -c rainix-rs-prelude) +(cd lib/rain.tofu.erc20-decimals && nix develop -c forge build) nix develop -c rainix-sol-prelude nix develop -c rainix-rs-prelude diff --git a/prep-all.sh b/prep-all.sh index e865db53d3..61a1a50c5b 100755 --- a/prep-all.sh +++ b/prep-all.sh @@ -35,6 +35,9 @@ nix develop -i ${keep[@]} -c bash \ nix develop -i ${keep[@]} -c bash \ -c '(cd lib/rain.interpreter/lib/rain.interpreter.interface/lib/rain.math.float && rainix-rs-prelude)' +echo "Setting up rain.tofu.erc20-decimals..." +(cd lib/rain.tofu.erc20-decimals && nix develop -c forge build) + echo "Setting up rain.interpreter..." nix develop -i ${keep[@]} -c bash -c '(cd lib/rain.interpreter && rainix-sol-prelude)' nix develop -i ${keep[@]} -c bash -c '(cd lib/rain.interpreter && rainix-rs-prelude)' diff --git a/slither.config.json b/slither.config.json index 9a54a934dd..e463555647 100644 --- a/slither.config.json +++ b/slither.config.json @@ -1,4 +1,4 @@ { "detectors_to_exclude": "assembly-usage,solc-version,unused-imports,different-pragma-directives-are-used", - "filter_paths": "forge-std,openzeppelin,test,rain.math.float" + "filter_paths": "forge-std,openzeppelin,test,rain.math.float,rain.tofu.erc20-decimals" } diff --git a/src/abstract/OrderBookV5ArbOrderTaker.sol b/src/abstract/OrderBookV5ArbOrderTaker.sol index 4a2cf50388..330440e9e0 100644 --- a/src/abstract/OrderBookV5ArbOrderTaker.sol +++ b/src/abstract/OrderBookV5ArbOrderTaker.sol @@ -29,7 +29,7 @@ import {LibBytecode} from "rain.interpreter.interface/lib/bytecode/LibBytecode.s import {LibOrderBook} from "../lib/LibOrderBook.sol"; import {LibOrderBookArb, NonZeroBeforeArbStack, BadLender} from "../lib/LibOrderBookArb.sol"; import {IOrderBookV5OrderTaker} from "rain.orderbook.interface/interface/unstable/IOrderBookV5OrderTaker.sol"; -import {LibTOFUTokenDecimals, TOFUTokenDecimals} from "../lib/LibTOFUTokenDecimals.sol"; +import {LibTOFUTokenDecimals} from "rain.tofu.erc20-decimals/lib/LibTOFUTokenDecimals.sol"; /// Thrown when "before arb" wants inputs that we don't have. error NonZeroBeforeArbInputs(uint256 inputs); @@ -47,8 +47,6 @@ abstract contract OrderBookV5ArbOrderTaker is { using SafeERC20 for IERC20; - mapping(address token => TOFUTokenDecimals tofuTokenDecimals) internal sTOFUTokenDecimals; - constructor(OrderBookV5ArbConfig memory config) OrderBookV5ArbCommon(config) {} /// @inheritdoc IERC165 @@ -81,9 +79,9 @@ abstract contract OrderBookV5ArbOrderTaker is LibOrderBookArb.finalizeArb( task, ordersInputToken, - LibTOFUTokenDecimals.safeDecimalsForToken(sTOFUTokenDecimals, ordersInputToken), + LibTOFUTokenDecimals.safeDecimalsForToken(ordersInputToken), ordersOutputToken, - LibTOFUTokenDecimals.safeDecimalsForToken(sTOFUTokenDecimals, ordersOutputToken) + LibTOFUTokenDecimals.safeDecimalsForToken(ordersOutputToken) ); } diff --git a/src/abstract/OrderBookV5FlashBorrower.sol b/src/abstract/OrderBookV5FlashBorrower.sol index 36a2669e03..ac65ef63ad 100644 --- a/src/abstract/OrderBookV5FlashBorrower.sol +++ b/src/abstract/OrderBookV5FlashBorrower.sol @@ -26,7 +26,7 @@ import {OrderBookV5ArbConfig, OrderBookV5ArbCommon} from "./OrderBookV5ArbCommon import {EvaluableV4, SignedContextV1} from "rain.interpreter.interface/interface/unstable/IInterpreterCallerV4.sol"; import {LibOrderBook} from "../lib/LibOrderBook.sol"; import {LibOrderBookArb, NonZeroBeforeArbStack, BadLender} from "../lib/LibOrderBookArb.sol"; -import {LibTOFUTokenDecimals, TOFUTokenDecimals} from "../lib/LibTOFUTokenDecimals.sol"; +import {LibTOFUTokenDecimals} from "rain.tofu.erc20-decimals/lib/LibTOFUTokenDecimals.sol"; import {LibDecimalFloat} from "rain.math.float/lib/LibDecimalFloat.sol"; /// Thrown when the initiator is not the order book. @@ -73,8 +73,6 @@ abstract contract OrderBookV5FlashBorrower is IERC3156FlashBorrower, ReentrancyG using Address for address; using SafeERC20 for IERC20; - mapping(address token => TOFUTokenDecimals tofuTokenDecimals) internal sTOFUTokenDecimals; - constructor(OrderBookV5ArbConfig memory config) OrderBookV5ArbCommon(config) {} /// @inheritdoc IERC165 @@ -160,8 +158,8 @@ abstract contract OrderBookV5FlashBorrower is IERC3156FlashBorrower, ReentrancyG address ordersOutputToken = takeOrders.orders[0].order.validOutputs[takeOrders.orders[0].outputIOIndex].token; address ordersInputToken = takeOrders.orders[0].order.validInputs[takeOrders.orders[0].inputIOIndex].token; - uint8 inputDecimals = LibTOFUTokenDecimals.safeDecimalsForToken(sTOFUTokenDecimals, ordersInputToken); - uint8 outputDecimals = LibTOFUTokenDecimals.safeDecimalsForToken(sTOFUTokenDecimals, ordersOutputToken); + uint8 inputDecimals = LibTOFUTokenDecimals.safeDecimalsForToken(ordersInputToken); + uint8 outputDecimals = LibTOFUTokenDecimals.safeDecimalsForToken(ordersOutputToken); // We can't repay more than the minimum that the orders are going to // give us and there's no reason to borrow less. diff --git a/src/concrete/ob/OrderBook.sol b/src/concrete/ob/OrderBook.sol index b6487a2513..490fe7b68d 100644 --- a/src/concrete/ob/OrderBook.sol +++ b/src/concrete/ob/OrderBook.sol @@ -33,10 +33,9 @@ import {LibOrderBook} from "../../lib/LibOrderBook.sol"; import {LibDecimalFloat} from "rain.math.float/lib/LibDecimalFloat.sol"; import { LibTOFUTokenDecimals, - TOFUTokenDecimals, TOFUOutcome, TokenDecimalsReadFailure -} from "../../lib/LibTOFUTokenDecimals.sol"; +} from "rain.tofu.erc20-decimals/lib/LibTOFUTokenDecimals.sol"; import { IOrderBookV5, @@ -222,8 +221,6 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order //solhint-disable-next-line private-vars-leading-underscore mapping(bytes32 orderHash => uint256 liveness) internal sOrders; - mapping(address token => TOFUTokenDecimals tofuTokenDecimals) internal sTOFUTokenDecimals; - /// @dev Vault balances are stored in a mapping of owner => token => vault ID /// This gives 1:1 parity with the `IOrderBookV1` interface but keeping the /// `sFoo` naming convention for storage variables. @@ -386,6 +383,12 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order } } + function checkTokenSelfTrade(OrderV4 memory order, uint256 inputIOIndex, uint256 outputIOIndex) internal pure { + if (order.validInputs[inputIOIndex].token == order.validOutputs[outputIOIndex].token) { + revert TokenSelfTrade(); + } + } + /// @inheritdoc IOrderBookV5 function quote2(QuoteV2 calldata quoteConfig) external view returns (bool, Float, Float) { bytes32 orderHash = quoteConfig.order.hash(); @@ -394,12 +397,7 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order return (false, Float.wrap(0), Float.wrap(0)); } - if ( - quoteConfig.order.validInputs[quoteConfig.inputIOIndex].token - == quoteConfig.order.validOutputs[quoteConfig.outputIOIndex].token - ) { - revert TokenSelfTrade(); - } + checkTokenSelfTrade(quoteConfig.order, quoteConfig.inputIOIndex, quoteConfig.outputIOIndex); OrderIOCalculationV4 memory orderIOCalculation = calculateOrderIO( quoteConfig.order, @@ -426,6 +424,8 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order TakeOrderConfigV4 memory takeOrderConfig; OrderV4 memory order; + address orderInputToken = config.orders[0].order.validInputs[config.orders[0].inputIOIndex].token; + address orderOutputToken = config.orders[0].order.validOutputs[config.orders[0].outputIOIndex].token; // Allocate a region of memory to hold pointers. We don't know how many // will run at this point, but we conservatively set aside a slot for @@ -456,24 +456,13 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order // Every order needs the same input token. // Every order needs the same output token. if ( - ( - order.validInputs[takeOrderConfig.inputIOIndex].token - != config.orders[0].order.validInputs[config.orders[0].inputIOIndex].token - ) - || ( - order.validOutputs[takeOrderConfig.outputIOIndex].token - != config.orders[0].order.validOutputs[config.orders[0].outputIOIndex].token - ) + (order.validInputs[takeOrderConfig.inputIOIndex].token != orderInputToken) + || (order.validOutputs[takeOrderConfig.outputIOIndex].token != orderOutputToken) ) { revert TokenMismatch(); } - if ( - order.validInputs[takeOrderConfig.inputIOIndex].token - == order.validOutputs[takeOrderConfig.outputIOIndex].token - ) { - revert TokenSelfTrade(); - } + checkTokenSelfTrade(order, takeOrderConfig.inputIOIndex, takeOrderConfig.outputIOIndex); bytes32 orderHash = order.hash(); if (sOrders[orderHash] == ORDER_DEAD) { @@ -530,10 +519,8 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order } } - { - if (totalTakerInput.lt(config.minimumInput)) { - revert MinimumInput(config.minimumInput, totalTakerInput); - } + if (totalTakerInput.lt(config.minimumInput)) { + revert MinimumInput(config.minimumInput, totalTakerInput); } // We send the tokens to `msg.sender` first adopting a similar pattern to @@ -547,19 +534,15 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order // external data (e.g. prices) that could be modified by the caller's // trades. - pushTokens(config.orders[0].order.validOutputs[config.orders[0].outputIOIndex].token, totalTakerInput); + pushTokens(orderOutputToken, totalTakerInput); if (config.data.length > 0) { IOrderBookV5OrderTaker(msg.sender).onTakeOrders2( - config.orders[0].order.validOutputs[config.orders[0].outputIOIndex].token, - config.orders[0].order.validInputs[config.orders[0].inputIOIndex].token, - totalTakerInput, - totalTakerOutput, - config.data + orderOutputToken, orderInputToken, totalTakerInput, totalTakerOutput, config.data ); } - pullTokens(config.orders[0].order.validInputs[config.orders[0].inputIOIndex].token, totalTakerOutput); + pullTokens(orderInputToken, totalTakerOutput); unchecked { for (uint256 i = 0; i < orderIOCalculationsToHandle.length; i++) { @@ -692,9 +675,8 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order ); { - (TOFUOutcome inputOutcome, uint8 inputDecimals) = LibTOFUTokenDecimals.decimalsForTokenReadOnly( - sTOFUTokenDecimals, order.validInputs[inputIOIndex].token - ); + (TOFUOutcome inputOutcome, uint8 inputDecimals) = + LibTOFUTokenDecimals.decimalsForTokenReadOnly(order.validInputs[inputIOIndex].token); if (inputOutcome != TOFUOutcome.Consistent && inputOutcome != TOFUOutcome.Initial) { revert TokenDecimalsReadFailure(order.validInputs[inputIOIndex].token, inputOutcome); } @@ -712,9 +694,8 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order } { - (TOFUOutcome outputOutcome, uint8 outputDecimals) = LibTOFUTokenDecimals.decimalsForTokenReadOnly( - sTOFUTokenDecimals, order.validOutputs[outputIOIndex].token - ); + (TOFUOutcome outputOutcome, uint8 outputDecimals) = + LibTOFUTokenDecimals.decimalsForTokenReadOnly(order.validOutputs[outputIOIndex].token); if (outputOutcome != TOFUOutcome.Consistent && outputOutcome != TOFUOutcome.Initial) { revert TokenDecimalsReadFailure(order.validOutputs[outputIOIndex].token, outputOutcome); } @@ -956,7 +937,7 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order } function pullTokens(address token, Float amount) internal returns (uint256, uint8) { - (TOFUOutcome tofuOutcome, uint8 decimals) = LibTOFUTokenDecimals.decimalsForToken(sTOFUTokenDecimals, token); + (TOFUOutcome tofuOutcome, uint8 decimals) = LibTOFUTokenDecimals.decimalsForToken(token); if (tofuOutcome != TOFUOutcome.Consistent && tofuOutcome != TOFUOutcome.Initial) { revert TokenDecimalsReadFailure(token, tofuOutcome); } @@ -978,7 +959,7 @@ contract OrderBook is IOrderBookV5, IMetaV1_2, ReentrancyGuard, Multicall, Order } function pushTokens(address token, Float amountFloat) internal returns (uint256, uint8) { - (TOFUOutcome tofuOutcome, uint8 decimals) = LibTOFUTokenDecimals.decimalsForToken(sTOFUTokenDecimals, token); + (TOFUOutcome tofuOutcome, uint8 decimals) = LibTOFUTokenDecimals.decimalsForToken(token); if (tofuOutcome != TOFUOutcome.Consistent && tofuOutcome != TOFUOutcome.Initial) { revert TokenDecimalsReadFailure(token, tofuOutcome); } diff --git a/src/generated/OrderBookSubParser.pointers.sol b/src/generated/OrderBookSubParser.pointers.sol index 898c2e0a07..1c131c8a94 100644 --- a/src/generated/OrderBookSubParser.pointers.sol +++ b/src/generated/OrderBookSubParser.pointers.sol @@ -1,3 +1,7 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity ^0.8.25; + // THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol // This file is committed to the repository because there is a circular @@ -5,10 +9,6 @@ // needs the pointers file to exist so that it can compile, and the pointers // file needs the contract to exist so that it can be compiled. -// SPDX-License-Identifier: LicenseRef-DCL-1.0 -// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -pragma solidity =0.8.25; - /// @dev Hash of the known bytecode. bytes32 constant BYTECODE_HASH = bytes32(0x7c8b8c827e772690a58491ad0b064e4eadf31c95511023abfea56990fef5d847); diff --git a/src/lib/LibTOFUTokenDecimals.sol b/src/lib/LibTOFUTokenDecimals.sol deleted file mode 100644 index 72e11b920e..0000000000 --- a/src/lib/LibTOFUTokenDecimals.sol +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-DCL-1.0 -// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -pragma solidity ^0.8.19; - -/// Thrown when a TOFU decimals read fails during deposit. -/// @param token The token that failed to read decimals. -/// @param tofuOutcome The outcome of the TOFU read. -error TokenDecimalsReadFailure(address token, TOFUOutcome tofuOutcome); - -/// Encodes the token's decimals for a token. Includes a bool to indicate if -/// the token's decimals have been read from the external contract before. This -/// guards against the default `0` value for unset storage data being -/// misinterpreted as a valid token decimal value `0`. -/// @param initialized True if the token's decimals have been read from the -/// external contract before. -/// @param tokenDecimals The token's decimals. -struct TOFUTokenDecimals { - bool initialized; - uint8 tokenDecimals; -} - -enum TOFUOutcome { - /// Token's decimals have not been read from the external contract before. - Initial, - /// Token's decimals are consistent with the stored value. - Consistent, - /// Token's decimals are inconsistent with the stored value. - Inconsistent, - /// Token's decimals could not be read from the external contract. - ReadFailure -} - -/// @dev The selector for the `decimals()` function in the ERC20 standard. -bytes constant TOFU_DECIMALS_SELECTOR = hex"313ce567"; - -library LibTOFUTokenDecimals { - function decimalsForTokenReadOnly(mapping(address => TOFUTokenDecimals) storage sTOFUTokenDecimals, address token) - internal - view - returns (TOFUOutcome, uint8) - { - TOFUTokenDecimals memory tofuTokenDecimals = sTOFUTokenDecimals[token]; - - // The default solidity try/catch logic will error if the return is a - // success but fails to deserialize to the target type. We need to handle - // all errors as read failures so that the calling context can decide - // whether to revert the current transaction or continue with the stored - // value. E.g. withdrawals will prefer to continue than trap funds, and - // deposits will prefer to revert and prevent new funds entering the - // DEX. - //slither-disable-start low-level-calls - //slither-disable-start calls-loop - (bool success, bytes memory returnData) = token.staticcall(TOFU_DECIMALS_SELECTOR); - //slither-disable-end low-level-calls - //slither-disable-end calls-loop - if (!success || returnData.length != 0x20) { - return (TOFUOutcome.ReadFailure, tofuTokenDecimals.tokenDecimals); - } - - uint256 decodedDecimals = abi.decode(returnData, (uint256)); - if (decodedDecimals > type(uint8).max) { - return (TOFUOutcome.ReadFailure, tofuTokenDecimals.tokenDecimals); - } - uint8 readDecimals = uint8(decodedDecimals); - - if (!tofuTokenDecimals.initialized) { - return (TOFUOutcome.Initial, readDecimals); - } else { - return ( - readDecimals == tofuTokenDecimals.tokenDecimals ? TOFUOutcome.Consistent : TOFUOutcome.Inconsistent, - tofuTokenDecimals.tokenDecimals - ); - } - } - - /// Trust on first use (TOFU) token decimals. - /// The first time we read the decimals from a token we store them in a - /// mapping. If the token's decimals change we will always use the stored - /// value. This is because the token's decimals could technically change and - /// are NOT intended for onchain use as they are optional, but we're doing - /// it anyway to convert to floating point numbers. - /// - /// If we have nothing stored we read from the token, store and return it - /// with TOFUOUTCOME.Initial. - /// - /// If the call to `decimals` is not a success that deserializes cleanly to - /// a `uint8` we return the stored value and TOFUOUTCOME.ReadFailure. - /// - /// If the stored value is inconsistent with the token's decimals we return - /// the stored value and TOFUOUTCOME.Inconsistent. - function decimalsForToken(mapping(address => TOFUTokenDecimals) storage sTOFUTokenDecimals, address token) - internal - returns (TOFUOutcome, uint8) - { - (TOFUOutcome tofuOutcome, uint8 readDecimals) = decimalsForTokenReadOnly(sTOFUTokenDecimals, token); - - if (tofuOutcome == TOFUOutcome.Initial) { - sTOFUTokenDecimals[token] = TOFUTokenDecimals({initialized: true, tokenDecimals: readDecimals}); - } - return (tofuOutcome, readDecimals); - } - - /// Trust on first use (TOFU) token decimals. - /// Same as `decimalsForToken` but reverts with a standard error if the - /// token's decimals are inconsistent. On the first read the decimals are - /// never considered inconsistent. - /// @return The token's decimals. - function safeDecimalsForToken(mapping(address => TOFUTokenDecimals) storage sTOFUTokenDecimals, address token) - internal - returns (uint8) - { - (TOFUOutcome tofuOutcome, uint8 readDecimals) = decimalsForToken(sTOFUTokenDecimals, token); - if (tofuOutcome != TOFUOutcome.Consistent && tofuOutcome != TOFUOutcome.Initial) { - revert TokenDecimalsReadFailure(token, tofuOutcome); - } - return readDecimals; - } -} diff --git a/tauri-app/src-tauri/src/commands/order_take.rs b/tauri-app/src-tauri/src/commands/order_take.rs index 2fe3816071..8eba813de3 100644 --- a/tauri-app/src-tauri/src/commands/order_take.rs +++ b/tauri-app/src-tauri/src/commands/order_take.rs @@ -233,7 +233,7 @@ mod tests { let expected = " id,timestamp,timestamp_display,transaction,sender,order_id,input,input_display,input_token_id,input_token_symbol,output,output_display,output_token_id,output_token_symbol trade1,0,1970-01-01 00:00:00 UTC,tx1,sender1,hash1,0x0000000000000000000000000000000000000000000000000000000000000001,1,0x1d80c49bbbcd1c0911346656b529df9e5c2f783d,WFLR,0x00000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffe,-2,0x12e605bc104e93b45e1ad99f9e555f659051c2bb,sFLR -trade2,1700086400,2023-11-15 22:13:20 UTC,tx2,sender2,hash2,0x0000000000000000000000000000000000000000000000000000000000000002,2,0x1d80c49bbbcd1c0911346656b529df9e5c2f783d,WFLR,0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,-0.5,0x12e605bc104e93b45e1ad99f9e555f659051c2bb,sFLR +trade2,1700086400,2023-11-15 22:13:20 UTC,tx2,sender2,hash2,0x0000000000000000000000000000000000000000000000000000000000000002,2,0x1d80c49bbbcd1c0911346656b529df9e5c2f783d,WFLR,0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb,-5e-1,0x12e605bc104e93b45e1ad99f9e555f659051c2bb,sFLR "; let csv_text = fs::read_to_string(path.clone()).unwrap(); assert_eq!(csv_text.trim(), expected.trim()); diff --git a/tauri-app/src/lib/services/loadRemoteSettings.ts b/tauri-app/src/lib/services/loadRemoteSettings.ts index e931afac61..696b7289f4 100644 --- a/tauri-app/src/lib/services/loadRemoteSettings.ts +++ b/tauri-app/src/lib/services/loadRemoteSettings.ts @@ -1,5 +1,5 @@ const REMOTE_SETTINGS_URL = - 'https://raw.githubusercontent.com/rainlanguage/rain.strategies/e5fb0899864c9a2b084dd97312f78ccac1444cab/settings.yaml'; + 'https://raw.githubusercontent.com/rainlanguage/rain.strategies/d57c3aa77b63f90535957947d15146c0182367dc/settings.yaml'; export async function loadRemoteSettings() { const response = await fetch(REMOTE_SETTINGS_URL); diff --git a/test/concrete/ob/OrderBook.deposit.entask.t.sol b/test/concrete/ob/OrderBook.deposit.entask.t.sol index 45e3b171a5..bb183f8ff3 100644 --- a/test/concrete/ob/OrderBook.deposit.entask.t.sol +++ b/test/concrete/ob/OrderBook.deposit.entask.t.sol @@ -21,27 +21,17 @@ contract OrderBookDepositEnactTest is OrderBookExternalRealTest { using LibDecimalFloat for Float; using LibFormatDecimalFloat for Float; - uint256 internal runID = 0; - mapping(uint256 => bool) internal previouslyDeposited; - - constructor() { - runID++; - } - - function checkReentrancyRW() internal { - bool isFirstDeposit = !previouslyDeposited[runID]; - previouslyDeposited[runID] = true; - + function checkReentrancyRW() internal view { (bytes32[] memory reads, bytes32[] memory writes) = vm.accesses(address(iOrderbook)); // 3 reads for reentrancy guard. // 5 reads for deposit. - assertEq(reads.length, isFirstDeposit ? 8 : 6); + assertEq(reads.length, 5); assertEq(reads[0], bytes32(uint256(0))); assertEq(reads[1], bytes32(uint256(0))); assertEq(reads[reads.length - 1], bytes32(uint256(0))); // 2 writes for reentrancy guard. // 2 write for deposit. - assertEq(writes.length, isFirstDeposit ? 4 : 3); + assertEq(writes.length, 3); assertEq(writes[0], bytes32(uint256(0))); assertEq(writes[writes.length - 1], bytes32(uint256(0))); } @@ -232,7 +222,7 @@ contract OrderBookDepositEnactTest is OrderBookExternalRealTest { string.concat( usingWordsFrom, ":ensure(equal-to(deposit-vault-before() ", - preDepositAmount.toDecimalString(9), + preDepositAmount.toDecimalString(false), ") \"vault balance before\");" ) ); @@ -240,7 +230,7 @@ contract OrderBookDepositEnactTest is OrderBookExternalRealTest { string.concat( usingWordsFrom, ":ensure(equal-to(deposit-vault-after() ", - preDepositAmount.add(depositAmount).toDecimalString(9), + preDepositAmount.add(depositAmount).toDecimalString(false), ") \"vault balance after\");" ) ); diff --git a/test/concrete/ob/OrderBook.deposit.t.sol b/test/concrete/ob/OrderBook.deposit.t.sol index 277a39b460..fe44ff2a1f 100644 --- a/test/concrete/ob/OrderBook.deposit.t.sol +++ b/test/concrete/ob/OrderBook.deposit.t.sol @@ -163,7 +163,7 @@ contract OrderBookDepositTest is OrderBookExternalMockTest { // - reentrancy guard x3 // - vault balance floats x2 // - token decimals x2 - assertTrue(reads.length == 6 || reads.length == 8, "reads"); + assertTrue(reads.length == 5, "reads"); // // - reentrancy guard x2 // // - vault balance x1 assertTrue(writes.length == 4 || writes.length == 3, "writes"); diff --git a/test/concrete/ob/OrderBook.withdraw.entask.t.sol b/test/concrete/ob/OrderBook.withdraw.entask.t.sol index 8d96237355..2dfae7464e 100644 --- a/test/concrete/ob/OrderBook.withdraw.entask.t.sol +++ b/test/concrete/ob/OrderBook.withdraw.entask.t.sol @@ -114,7 +114,7 @@ contract OrderBookWithdrawEvalTest is OrderBookExternalRealTest { } iOrderbook.withdraw3(address(iToken0), vaultId, targetAmount, actions); if (err.length == 0) { - checkReentrancyRW(7, 3); + checkReentrancyRW(6, 3); (bytes32[] memory reads, bytes32[] memory writes) = vm.accesses(address(iStore)); assertEq(reads.length, expectedReads); assertEq(writes.length, expectedWrites); @@ -377,7 +377,7 @@ contract OrderBookWithdrawEvalTest is OrderBookExternalRealTest { string.concat( usingWordsFrom, ":ensure(equal-to(withdraw-vault-before() ", - depositAmount.toDecimalString(9), + depositAmount.toDecimalString(false), ") \"vault before\");" ) ); @@ -385,7 +385,7 @@ contract OrderBookWithdrawEvalTest is OrderBookExternalRealTest { string.concat( usingWordsFrom, ":ensure(equal-to(withdraw-vault-after() ", - depositAmount.sub(withdrawAmount).toDecimalString(9), + depositAmount.sub(withdrawAmount).toDecimalString(false), ") \"balance after\");" ) ); @@ -394,7 +394,7 @@ contract OrderBookWithdrawEvalTest is OrderBookExternalRealTest { string.concat( usingWordsFrom, ":ensure(equal-to(withdraw-target-amount() ", - targetAmount.toDecimalString(9), + targetAmount.toDecimalString(false), ") \"target amount\");" ) ); diff --git a/test/concrete/ob/OrderBook.withdraw.t.sol b/test/concrete/ob/OrderBook.withdraw.t.sol index 3293f7489d..67ce30abaa 100644 --- a/test/concrete/ob/OrderBook.withdraw.t.sol +++ b/test/concrete/ob/OrderBook.withdraw.t.sol @@ -44,8 +44,8 @@ contract OrderBookWithdrawTest is OrderBookExternalMockTest { vm.record(); iOrderbook.withdraw3(address(iToken0), vaultId, amount, new TaskV2[](0)); (bytes32[] memory reads, bytes32[] memory writes) = vm.accesses(address(iOrderbook)); - assertEq(reads.length, 9, "reads"); - assertEq(writes.length, 4, "writes"); + assertEq(reads.length, 6, "reads"); + assertEq(writes.length, 3, "writes"); } /// Withdrawing the full amount from a vault should delete the vault. diff --git a/test/util/abstract/ArbTest.sol b/test/util/abstract/ArbTest.sol index f6bcd3b5cb..6abec678d8 100644 --- a/test/util/abstract/ArbTest.sol +++ b/test/util/abstract/ArbTest.sol @@ -21,6 +21,7 @@ import {OrderBookV5ArbConfig} from "src/concrete/arb/GenericPoolOrderBookV5ArbOr import {TaskV2} from "rain.orderbook.interface/interface/unstable/IOrderBookV5.sol"; import {IInterpreterV4} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; import {IInterpreterStoreV3} from "rain.interpreter.interface/interface/unstable/IInterpreterStoreV3.sol"; +import {TOFUTokenDecimals, LibTOFUTokenDecimals} from "rain.tofu.erc20-decimals/concrete/TOFUTokenDecimals.sol"; contract Token is ERC20 { constructor() ERC20("Token", "TKN") {} @@ -50,6 +51,10 @@ abstract contract ArbTest is Test { function buildArb(OrderBookV5ArbConfig memory config) internal virtual returns (address); constructor() { + // Put the TOFU decimals contract in place so that any calls to it + // succeed. This is because we don't have zoltu here. + vm.etch(address(LibTOFUTokenDecimals.TOFU_DECIMALS_DEPLOYMENT), type(TOFUTokenDecimals).runtimeCode); + iInterpreter = IInterpreterV4(address(uint160(uint256(keccak256("interpreter.rain.test"))))); vm.label(address(iInterpreter), "iInterpreter"); iInterpreterStore = IInterpreterStoreV3(address(uint160(uint256(keccak256("interpreter.store.rain.test"))))); diff --git a/test/util/abstract/OrderBookExternalMockTest.sol b/test/util/abstract/OrderBookExternalMockTest.sol index 451298b84c..51a5dbec82 100644 --- a/test/util/abstract/OrderBookExternalMockTest.sol +++ b/test/util/abstract/OrderBookExternalMockTest.sol @@ -19,6 +19,7 @@ import {LibOrder} from "src/lib/LibOrder.sol"; import {OrderBook} from "src/concrete/ob/OrderBook.sol"; import {EvaluableV4} from "rain.interpreter.interface/interface/unstable/IInterpreterCallerV4.sol"; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {TOFUTokenDecimals, LibTOFUTokenDecimals} from "rain.tofu.erc20-decimals/concrete/TOFUTokenDecimals.sol"; /// @title OrderBookExternalTest /// Abstract contract that performs common setup needed for testing an orderbook @@ -40,6 +41,11 @@ abstract contract OrderBookExternalMockTest is Test, IMetaV1_2, IOrderBookV5Stub constructor() { vm.pauseGasMetering(); + + // Put the TOFU decimals contract in place so that any calls to it + // succeed. This is because we don't have zoltu here. + vm.etch(address(LibTOFUTokenDecimals.TOFU_DECIMALS_DEPLOYMENT), type(TOFUTokenDecimals).runtimeCode); + iInterpreter = IInterpreterV4(address(uint160(uint256(keccak256("interpreter.rain.test"))))); vm.etch(address(iInterpreter), REVERTING_MOCK_BYTECODE); iStore = IInterpreterStoreV3(address(uint160(uint256(keccak256("store.rain.test"))))); diff --git a/test/util/abstract/OrderBookExternalRealTest.sol b/test/util/abstract/OrderBookExternalRealTest.sol index 82f8d2cf8b..2e7b7ddde0 100644 --- a/test/util/abstract/OrderBookExternalRealTest.sol +++ b/test/util/abstract/OrderBookExternalRealTest.sol @@ -26,6 +26,7 @@ import {RainterpreterParser} from "rain.interpreter/concrete/RainterpreterParser import {OrderBookSubParser} from "src/concrete/parser/OrderBookSubParser.sol"; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {LibDecimalFloat, Float} from "rain.math.float/lib/LibDecimalFloat.sol"; +import {TOFUTokenDecimals, LibTOFUTokenDecimals} from "rain.tofu.erc20-decimals/concrete/TOFUTokenDecimals.sol"; abstract contract OrderBookExternalRealTest is Test, IOrderBookV5Stub { IInterpreterV4 internal immutable iInterpreter; @@ -38,6 +39,10 @@ abstract contract OrderBookExternalRealTest is Test, IOrderBookV5Stub { OrderBookSubParser internal immutable iSubParser; constructor() { + // Put the TOFU decimals contract in place so that any calls to it + // succeed. This is because we don't have zoltu here. + vm.etch(address(LibTOFUTokenDecimals.TOFU_DECIMALS_DEPLOYMENT), type(TOFUTokenDecimals).runtimeCode); + iInterpreter = IInterpreterV4(new Rainterpreter()); iStore = IInterpreterStoreV3(new RainterpreterStore()); iParser = new RainterpreterParser();