diff --git a/.gitmodules b/.gitmodules index 257ba39..9eccac5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,4 +4,7 @@ [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts - branch = v5.0.2 \ No newline at end of file + branch = v5.0.2 +[submodule "lib/abdk-libraries-solidity"] + path = lib/abdk-libraries-solidity + url = https://github.com/abdk-consulting/abdk-libraries-solidity diff --git a/lib/abdk-libraries-solidity b/lib/abdk-libraries-solidity new file mode 160000 index 0000000..5e1e7c1 --- /dev/null +++ b/lib/abdk-libraries-solidity @@ -0,0 +1 @@ +Subproject commit 5e1e7c11b35f8313d3f7ce11c1b86320d7c0b554 diff --git a/reviews/202407-gnosis/01-report.md b/reviews/202407-gnosis/01-report.md new file mode 100644 index 0000000..3a8ab9d --- /dev/null +++ b/reviews/202407-gnosis/01-report.md @@ -0,0 +1,5 @@ +# External review of v0.3.4-alpha + +Report done by internal Gnosis review team. + +https://gnosischain.notion.site/CirclesV2-Audit-2f846eba6e2c4540a256f518506428ad diff --git a/reviews/202407-gnosis/02-reply.md b/reviews/202407-gnosis/02-reply.md new file mode 100644 index 0000000..175eb4a --- /dev/null +++ b/reviews/202407-gnosis/02-reply.md @@ -0,0 +1,16 @@ +# Addressing review comments + +## Attacker can steal tokens from user who opted out consented flow + +### Summary + +If any user opts out of consented flow by calling `setAdvancedUsageFlag()` with argument `ADVANCED_FLAG_OPTOUT_CONSENTEDFLOW` they accept that parties they do not trust may hold their tokens due to their tokens being used as a transition in a flow matrix. Due to the way group tokens are minted when operating a flow matrix, any group can steal every tokens held by a user. Without consented flow, an attacker can deploy a group and steal tokens from the user without requiring the trust of the user. + +(for full description, see report) + +### Response strategy: +1. consider only consented flow, ie. remove ability to opt-out. Even if 3. is adopted as the elegant fix for netting the flow matrix correctly over group mints, it still leaves open the first premise of the severe attack: "Attacker can steal tokens from user who opted out consented flow", which matters once different Circles identifiers have market price valuations. +2. consider requiring the flow operator must be authorised for all touched vertices: + - (re)imposing this restriction makes it a lot harder to iterate on improvements for flow operators; + - on the other hand, enabling it would be a strong mechanism against yet unknown exploits in the protocol. This however needs to be balanced with our confidence in the correctness of the implementation. +3. consider improving how the flow matrix should be netted over group mints along a flow path to remove the error of nullable path edges. \ No newline at end of file diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/chiado-artefacts-rc-0.3.5-alpha-1d343b7-240808-180634.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/chiado-artefacts-rc-0.3.5-alpha-1d343b7-240808-180634.txt new file mode 100644 index 0000000..77e0f21 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/chiado-artefacts-rc-0.3.5-alpha-1d343b7-240808-180634.txt @@ -0,0 +1,9 @@ +{"contractName":"Hub","deployedAddress":"0x4360449bAfbA60660125e4306960AaF23Dc90aaa","sourcePath":"src/hub/Hub.sol:Hub","constructor-args":"0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0x92132109fAEFb69a307c53Ce3506CA9DAF12c96c 0x3d22c0db967d4eF3A135AAF37b93D2DA1E631AF7 0xD1a83E3ae4817096Ff448BE2f0C4432B34bCf33D 0x7Fb40eEcC73F7ac2F24B43f1A827A0e8F98FEa34 1675209600 31540000 https://fallback.aboutcircles.com/v1/circles/{id}.json","argumentsFile":"constructorArgs_Hub.txt"} +{"contractName":"Migration","deployedAddress":"0x3d22c0db967d4eF3A135AAF37b93D2DA1E631AF7","sourcePath":"src/migration/Migration.sol:Migration","constructor-args":"0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0x4360449bAfbA60660125e4306960AaF23Dc90aaa 1675209600","argumentsFile":"constructorArgs_Migration.txt"} +{"contractName":"NameRegistry","deployedAddress":"0x92132109fAEFb69a307c53Ce3506CA9DAF12c96c","sourcePath":"src/names/NameRegistry.sol:NameRegistry","constructor-args":"0x4360449bAfbA60660125e4306960AaF23Dc90aaa","argumentsFile":"constructorArgs_NameRegistry.txt"} +{"contractName":"ERC20Lift","deployedAddress":"0xD1a83E3ae4817096Ff448BE2f0C4432B34bCf33D","sourcePath":"src/lift/ERC20Lift.sol:ERC20Lift","constructor-args":"0x4360449bAfbA60660125e4306960AaF23Dc90aaa 0x92132109fAEFb69a307c53Ce3506CA9DAF12c96c 0x3da7E15b21fFA1355e74eD0b4C98Cc2A8ab37772 0xeaBE5f7911e2A084fb57Ef26F1a159c1219215Dc","argumentsFile":"constructorArgs_ERC20Lift.txt"} +{"contractName":"StandardTreasury","deployedAddress":"0x7Fb40eEcC73F7ac2F24B43f1A827A0e8F98FEa34","sourcePath":"src/treasury/StandardTreasury.sol:StandardTreasury","constructor-args":"0x4360449bAfbA60660125e4306960AaF23Dc90aaa 0xbFE9AE97853cCdCB11C954337237bc3492D2e618","argumentsFile":"constructorArgs_StandardTreasury.txt"} +{"contractName":"BaseGroupMintPolicy","deployedAddress":"0xc96F273676278105cC6105C1594DF4C20A1B8c34","sourcePath":"src/groups/BaseMintPolicy.sol:MintPolicy","constructor-args":"","argumentsFile":"constructorArgs_BaseGroupMintPolicy.txt"} +{"contractName":"MastercopyDemurrageERC20","deployedAddress":"0x3da7E15b21fFA1355e74eD0b4C98Cc2A8ab37772","sourcePath":"src/lift/DemurrageCircles.sol:DemurrageCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyDemurrageERC20.txt"} +{"contractName":"MastercopyInflationaryERC20","deployedAddress":"0xeaBE5f7911e2A084fb57Ef26F1a159c1219215Dc","sourcePath":"src/lift/InflationaryCircles.sol:InflationaryCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyInflationaryERC20.txt"} +{"contractName":"MastercopyStandardVault","deployedAddress":"0xbFE9AE97853cCdCB11C954337237bc3492D2e618","sourcePath":"src/treasury/StandardVault.sol:StandardVault","constructor-args":"","argumentsFile":"constructorArgs_MastercopyStandardVault.txt"} diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/chiado-rc-0.3.5-alpha-1d343b7-240808-180634.log b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/chiado-rc-0.3.5-alpha-1d343b7-240808-180634.log new file mode 100644 index 0000000..243f510 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/chiado-rc-0.3.5-alpha-1d343b7-240808-180634.log @@ -0,0 +1,18 @@ +Chiado deployment +================= +Deployment Date: 2024-08-08 18:06:34 +Version: rc-0.3.5-alpha +Git Commit: 1d343b7fa8501c0347568df9318414f630cd585d +Deployer Address: 0x7619F26728Ced663E50E578EB6ff42430931564c, Intitial nonce: 140 +Compiler Version: v0.8.23+commit.f704f362 + +Deployed Contracts: +Hub: 0x4360449bAfbA60660125e4306960AaF23Dc90aaa +Migration: 0x3d22c0db967d4eF3A135AAF37b93D2DA1E631AF7 +NameRegistry: 0x92132109fAEFb69a307c53Ce3506CA9DAF12c96c +ERC20Lift: 0xD1a83E3ae4817096Ff448BE2f0C4432B34bCf33D +StandardTreasury: 0x7Fb40eEcC73F7ac2F24B43f1A827A0e8F98FEa34 +BaseGroupMintPolicy: 0xc96F273676278105cC6105C1594DF4C20A1B8c34 +MastercopyDemurrageERC20: 0x3da7E15b21fFA1355e74eD0b4C98Cc2A8ab37772 +MastercopyInflationaryERC20: 0xeaBE5f7911e2A084fb57Ef26F1a159c1219215Dc +MastercopyStandardVault: 0xbFE9AE97853cCdCB11C954337237bc3492D2e618 diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_BaseGroupMintPolicy.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_BaseGroupMintPolicy.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_BaseGroupMintPolicy.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_ERC20Lift.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_ERC20Lift.txt new file mode 100644 index 0000000..62b4555 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_ERC20Lift.txt @@ -0,0 +1 @@ +0x4360449bAfbA60660125e4306960AaF23Dc90aaa 0x92132109fAEFb69a307c53Ce3506CA9DAF12c96c 0x3da7E15b21fFA1355e74eD0b4C98Cc2A8ab37772 0xeaBE5f7911e2A084fb57Ef26F1a159c1219215Dc diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_Hub.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_Hub.txt new file mode 100644 index 0000000..8a4e5ba --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_Hub.txt @@ -0,0 +1 @@ +0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0x92132109fAEFb69a307c53Ce3506CA9DAF12c96c 0x3d22c0db967d4eF3A135AAF37b93D2DA1E631AF7 0xD1a83E3ae4817096Ff448BE2f0C4432B34bCf33D 0x7Fb40eEcC73F7ac2F24B43f1A827A0e8F98FEa34 1675209600 31540000 https://fallback.aboutcircles.com/v1/circles/{id}.json diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyDemurrageERC20.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyDemurrageERC20.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyDemurrageERC20.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyInflationaryERC20.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyInflationaryERC20.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyInflationaryERC20.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyStandardVault.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyStandardVault.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_MastercopyStandardVault.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_Migration.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_Migration.txt new file mode 100644 index 0000000..de69d48 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_Migration.txt @@ -0,0 +1 @@ +0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0x4360449bAfbA60660125e4306960AaF23Dc90aaa 1675209600 diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_NameRegistry.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_NameRegistry.txt new file mode 100644 index 0000000..d4ca7da --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_NameRegistry.txt @@ -0,0 +1 @@ +0x4360449bAfbA60660125e4306960AaF23Dc90aaa diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_StandardTreasury.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_StandardTreasury.txt new file mode 100644 index 0000000..e430e97 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-180634/constructorArgs_StandardTreasury.txt @@ -0,0 +1 @@ +0x4360449bAfbA60660125e4306960AaF23Dc90aaa 0xbFE9AE97853cCdCB11C954337237bc3492D2e618 diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/chiado-artefacts-rc-0.3.5-alpha-1d343b7-240808-183621.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/chiado-artefacts-rc-0.3.5-alpha-1d343b7-240808-183621.txt new file mode 100644 index 0000000..d73ea3b --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/chiado-artefacts-rc-0.3.5-alpha-1d343b7-240808-183621.txt @@ -0,0 +1,9 @@ +{"contractName":"Hub","deployedAddress":"0xEddc960D3c78692BF38577054cb0a35114AE35e0","sourcePath":"src/hub/Hub.sol:Hub","constructor-args":"0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0x5525cbF9ad01a4E805ed1b40723D6377b336eCcf 0x8C9BeAccb6b7DBd3AeffB5D77cab36b62Fe98882 0xd6407bc654a4de9b044b84dCfF9B77F0250fCba3 0x66A024F2055fa84b40f27c2f3Eb68A848276A641 1675209600 31540000 https://gateway.aboutcircles.com/v1/circles/{id}.json","argumentsFile":"constructorArgs_Hub.txt"} +{"contractName":"Migration","deployedAddress":"0x8C9BeAccb6b7DBd3AeffB5D77cab36b62Fe98882","sourcePath":"src/migration/Migration.sol:Migration","constructor-args":"0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0xEddc960D3c78692BF38577054cb0a35114AE35e0 1675209600","argumentsFile":"constructorArgs_Migration.txt"} +{"contractName":"NameRegistry","deployedAddress":"0x5525cbF9ad01a4E805ed1b40723D6377b336eCcf","sourcePath":"src/names/NameRegistry.sol:NameRegistry","constructor-args":"0xEddc960D3c78692BF38577054cb0a35114AE35e0","argumentsFile":"constructorArgs_NameRegistry.txt"} +{"contractName":"ERC20Lift","deployedAddress":"0xd6407bc654a4de9b044b84dCfF9B77F0250fCba3","sourcePath":"src/lift/ERC20Lift.sol:ERC20Lift","constructor-args":"0xEddc960D3c78692BF38577054cb0a35114AE35e0 0x5525cbF9ad01a4E805ed1b40723D6377b336eCcf 0xB3B6950411bE5b9411CdE616B669204A24569bD5 0xB04Ad230CBD53Faf0888de2e131467BD933F8237","argumentsFile":"constructorArgs_ERC20Lift.txt"} +{"contractName":"StandardTreasury","deployedAddress":"0x66A024F2055fa84b40f27c2f3Eb68A848276A641","sourcePath":"src/treasury/StandardTreasury.sol:StandardTreasury","constructor-args":"0xEddc960D3c78692BF38577054cb0a35114AE35e0 0xb2bEcDfEB4afcaB94Fa05b3822D35cD401C7897A","argumentsFile":"constructorArgs_StandardTreasury.txt"} +{"contractName":"BaseGroupMintPolicy","deployedAddress":"0xaD49f877021c73d00bE142b135c9AA67f0D8e9c6","sourcePath":"src/groups/BaseMintPolicy.sol:MintPolicy","constructor-args":"","argumentsFile":"constructorArgs_BaseGroupMintPolicy.txt"} +{"contractName":"MastercopyDemurrageERC20","deployedAddress":"0xB3B6950411bE5b9411CdE616B669204A24569bD5","sourcePath":"src/lift/DemurrageCircles.sol:DemurrageCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyDemurrageERC20.txt"} +{"contractName":"MastercopyInflationaryERC20","deployedAddress":"0xB04Ad230CBD53Faf0888de2e131467BD933F8237","sourcePath":"src/lift/InflationaryCircles.sol:InflationaryCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyInflationaryERC20.txt"} +{"contractName":"MastercopyStandardVault","deployedAddress":"0xb2bEcDfEB4afcaB94Fa05b3822D35cD401C7897A","sourcePath":"src/treasury/StandardVault.sol:StandardVault","constructor-args":"","argumentsFile":"constructorArgs_MastercopyStandardVault.txt"} diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/chiado-rc-0.3.5-alpha-1d343b7-240808-183621.log b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/chiado-rc-0.3.5-alpha-1d343b7-240808-183621.log new file mode 100644 index 0000000..cafc325 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/chiado-rc-0.3.5-alpha-1d343b7-240808-183621.log @@ -0,0 +1,18 @@ +Chiado deployment +================= +Deployment Date: 2024-08-08 18:36:21 +Version: rc-0.3.5-alpha +Git Commit: 1d343b7fa8501c0347568df9318414f630cd585d +Deployer Address: 0x7619F26728Ced663E50E578EB6ff42430931564c, Intitial nonce: 149 +Compiler Version: v0.8.23+commit.f704f362 + +Deployed Contracts: +Hub: 0xEddc960D3c78692BF38577054cb0a35114AE35e0 +Migration: 0x8C9BeAccb6b7DBd3AeffB5D77cab36b62Fe98882 +NameRegistry: 0x5525cbF9ad01a4E805ed1b40723D6377b336eCcf +ERC20Lift: 0xd6407bc654a4de9b044b84dCfF9B77F0250fCba3 +StandardTreasury: 0x66A024F2055fa84b40f27c2f3Eb68A848276A641 +BaseGroupMintPolicy: 0xaD49f877021c73d00bE142b135c9AA67f0D8e9c6 +MastercopyDemurrageERC20: 0xB3B6950411bE5b9411CdE616B669204A24569bD5 +MastercopyInflationaryERC20: 0xB04Ad230CBD53Faf0888de2e131467BD933F8237 +MastercopyStandardVault: 0xb2bEcDfEB4afcaB94Fa05b3822D35cD401C7897A diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_BaseGroupMintPolicy.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_BaseGroupMintPolicy.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_BaseGroupMintPolicy.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_ERC20Lift.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_ERC20Lift.txt new file mode 100644 index 0000000..a69b8c8 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_ERC20Lift.txt @@ -0,0 +1 @@ +0xEddc960D3c78692BF38577054cb0a35114AE35e0 0x5525cbF9ad01a4E805ed1b40723D6377b336eCcf 0xB3B6950411bE5b9411CdE616B669204A24569bD5 0xB04Ad230CBD53Faf0888de2e131467BD933F8237 diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_Hub.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_Hub.txt new file mode 100644 index 0000000..c0d8b4c --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_Hub.txt @@ -0,0 +1 @@ +0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0x5525cbF9ad01a4E805ed1b40723D6377b336eCcf 0x8C9BeAccb6b7DBd3AeffB5D77cab36b62Fe98882 0xd6407bc654a4de9b044b84dCfF9B77F0250fCba3 0x66A024F2055fa84b40f27c2f3Eb68A848276A641 1675209600 31540000 https://gateway.aboutcircles.com/v1/circles/{id}.json diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyDemurrageERC20.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyDemurrageERC20.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyDemurrageERC20.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyInflationaryERC20.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyInflationaryERC20.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyInflationaryERC20.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyStandardVault.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyStandardVault.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_MastercopyStandardVault.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_Migration.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_Migration.txt new file mode 100644 index 0000000..73ca5c5 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_Migration.txt @@ -0,0 +1 @@ +0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0xEddc960D3c78692BF38577054cb0a35114AE35e0 1675209600 diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_NameRegistry.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_NameRegistry.txt new file mode 100644 index 0000000..df899d5 --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_NameRegistry.txt @@ -0,0 +1 @@ +0xEddc960D3c78692BF38577054cb0a35114AE35e0 diff --git a/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_StandardTreasury.txt b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_StandardTreasury.txt new file mode 100644 index 0000000..755b4be --- /dev/null +++ b/script/deployments/chiado-rc-0.3.5-alpha-1d343b7-240808-183621/constructorArgs_StandardTreasury.txt @@ -0,0 +1 @@ +0xEddc960D3c78692BF38577054cb0a35114AE35e0 0xb2bEcDfEB4afcaB94Fa05b3822D35cD401C7897A diff --git a/script/deployments/chiadoDeploy.sh b/script/deployments/chiadoDeploy.sh index 94a631c..f33e1f2 100755 --- a/script/deployments/chiadoDeploy.sh +++ b/script/deployments/chiadoDeploy.sh @@ -77,7 +77,7 @@ INFLATION_DAY_ZERO=1675209600 # put a long bootstrap time for testing bootstrap BOOTSTRAP_ONE_YEAR=31540000 # fallback URI -URI='https://fallback.aboutcircles.com/v1/circles/{id}.json' +URI='https://gateway.aboutcircles.com/v1/circles/{id}.json' # re-export the variables for use here and in the general calculation JS script export PRIVATE_KEY=$PRIVATE_KEY_CHIADO diff --git a/script/deployments/gnosisChainDeploy.sh b/script/deployments/gnosisChainDeploy.sh index 28a622e..c34f8e3 100755 --- a/script/deployments/gnosisChainDeploy.sh +++ b/script/deployments/gnosisChainDeploy.sh @@ -99,7 +99,7 @@ INFLATION_DAY_ZERO=1602720000 # put a long bootstrap time for testing bootstrap to one year BOOTSTRAP_ONE_YEAR=31540000 # fallback URI -URI='https://fallback.aboutcircles.com/v1/circles/{id}.json' +URI='https://gateway.aboutcircles.com/v1/circles/{id}.json' # re-export the variables for use here and in the general calculation JS script export PRIVATE_KEY=$PRIVATE_KEY_GNOSIS diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_BaseGroupMintPolicy.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_BaseGroupMintPolicy.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_BaseGroupMintPolicy.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_ERC20Lift.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_ERC20Lift.txt new file mode 100644 index 0000000..79545ae --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_ERC20Lift.txt @@ -0,0 +1 @@ +0x7bC1F123089Bc1f384b6379d0587968d1CD5830a 0xb95eF3f3E693531d9588815bcA954dC8dce30937 0x3148182009D825aBfABfaeb11fc8F2e8a25dAc92 0x1b2fe34fCF2956A316936bC3F4E963fdaf9c1c72 diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_Hub.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_Hub.txt new file mode 100644 index 0000000..6df2075 --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_Hub.txt @@ -0,0 +1 @@ +0x29b9a7fBb8995b2423a71cC17cf9810798F6C543 0xb95eF3f3E693531d9588815bcA954dC8dce30937 0xEaBa6046103C3A2f5A681fD4323f78C647Fb4292 0xBB62B89fd96f42fe60216DCA3afefC67c95486d7 0x2434151eB40Af648AbcF73a6C9F1711FfF0F498B 1602720000 31540000 https://gateway.aboutcircles.com/v1/circles/{id}.json diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyDemurrageERC20.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyDemurrageERC20.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyDemurrageERC20.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyInflationaryERC20.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyInflationaryERC20.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyInflationaryERC20.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyStandardVault.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyStandardVault.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_MastercopyStandardVault.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_Migration.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_Migration.txt new file mode 100644 index 0000000..34b070a --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_Migration.txt @@ -0,0 +1 @@ +0x29b9a7fBb8995b2423a71cC17cf9810798F6C543 0x7bC1F123089Bc1f384b6379d0587968d1CD5830a 1602720000 diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_NameRegistry.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_NameRegistry.txt new file mode 100644 index 0000000..f6c1e74 --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_NameRegistry.txt @@ -0,0 +1 @@ +0x7bC1F123089Bc1f384b6379d0587968d1CD5830a diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_StandardTreasury.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_StandardTreasury.txt new file mode 100644 index 0000000..a3db43f --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/constructorArgs_StandardTreasury.txt @@ -0,0 +1 @@ +0x7bC1F123089Bc1f384b6379d0587968d1CD5830a 0x120DaC440c97d2D279988f056CCEf839B7E30409 diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/gnosischain-artefacts-rc-0.3.5-alpha-1d343b7-240808-185913.txt b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/gnosischain-artefacts-rc-0.3.5-alpha-1d343b7-240808-185913.txt new file mode 100644 index 0000000..cd1c49a --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/gnosischain-artefacts-rc-0.3.5-alpha-1d343b7-240808-185913.txt @@ -0,0 +1,9 @@ +{"contractName":"Hub","deployedAddress":"0x7bC1F123089Bc1f384b6379d0587968d1CD5830a","sourcePath":"src/hub/Hub.sol:Hub","constructor-args":"0x29b9a7fBb8995b2423a71cC17cf9810798F6C543 0xb95eF3f3E693531d9588815bcA954dC8dce30937 0xEaBa6046103C3A2f5A681fD4323f78C647Fb4292 0xBB62B89fd96f42fe60216DCA3afefC67c95486d7 0x2434151eB40Af648AbcF73a6C9F1711FfF0F498B 1602720000 31540000 https://gateway.aboutcircles.com/v1/circles/{id}.json","argumentsFile":"constructorArgs_Hub.txt"} +{"contractName":"Migration","deployedAddress":"0xEaBa6046103C3A2f5A681fD4323f78C647Fb4292","sourcePath":"src/migration/Migration.sol:Migration","constructor-args":"0x29b9a7fBb8995b2423a71cC17cf9810798F6C543 0x7bC1F123089Bc1f384b6379d0587968d1CD5830a 1602720000","argumentsFile":"constructorArgs_Migration.txt"} +{"contractName":"NameRegistry","deployedAddress":"0xb95eF3f3E693531d9588815bcA954dC8dce30937","sourcePath":"src/names/NameRegistry.sol:NameRegistry","constructor-args":"0x7bC1F123089Bc1f384b6379d0587968d1CD5830a","argumentsFile":"constructorArgs_NameRegistry.txt"} +{"contractName":"ERC20Lift","deployedAddress":"0xBB62B89fd96f42fe60216DCA3afefC67c95486d7","sourcePath":"src/lift/ERC20Lift.sol:ERC20Lift","constructor-args":"0x7bC1F123089Bc1f384b6379d0587968d1CD5830a 0xb95eF3f3E693531d9588815bcA954dC8dce30937 0x3148182009D825aBfABfaeb11fc8F2e8a25dAc92 0x1b2fe34fCF2956A316936bC3F4E963fdaf9c1c72","argumentsFile":"constructorArgs_ERC20Lift.txt"} +{"contractName":"StandardTreasury","deployedAddress":"0x2434151eB40Af648AbcF73a6C9F1711FfF0F498B","sourcePath":"src/treasury/StandardTreasury.sol:StandardTreasury","constructor-args":"0x7bC1F123089Bc1f384b6379d0587968d1CD5830a 0x120DaC440c97d2D279988f056CCEf839B7E30409","argumentsFile":"constructorArgs_StandardTreasury.txt"} +{"contractName":"BaseGroupMintPolicy","deployedAddress":"0x2470B43fc3303fCa660E68c86e3bEb8CE353C556","sourcePath":"src/groups/BaseMintPolicy.sol:MintPolicy","constructor-args":"","argumentsFile":"constructorArgs_BaseGroupMintPolicy.txt"} +{"contractName":"MastercopyDemurrageERC20","deployedAddress":"0x3148182009D825aBfABfaeb11fc8F2e8a25dAc92","sourcePath":"src/lift/DemurrageCircles.sol:DemurrageCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyDemurrageERC20.txt"} +{"contractName":"MastercopyInflationaryERC20","deployedAddress":"0x1b2fe34fCF2956A316936bC3F4E963fdaf9c1c72","sourcePath":"src/lift/InflationaryCircles.sol:InflationaryCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyInflationaryERC20.txt"} +{"contractName":"MastercopyStandardVault","deployedAddress":"0x120DaC440c97d2D279988f056CCEf839B7E30409","sourcePath":"src/treasury/StandardVault.sol:StandardVault","constructor-args":"","argumentsFile":"constructorArgs_MastercopyStandardVault.txt"} diff --git a/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913.log b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913.log new file mode 100644 index 0000000..1167513 --- /dev/null +++ b/script/deployments/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913/gnosischain-rc-0.3.5-alpha-1d343b7-240808-185913.log @@ -0,0 +1,18 @@ +Gnosis Chain deployment +================= +Deployment Date: 2024-08-08 18:59:13 +Version: rc-0.3.5-alpha +Git Commit: 1d343b7fa8501c0347568df9318414f630cd585d +Deployer Address: 0x7619F26728Ced663E50E578EB6ff42430931564c, Initial nonce: 47 +Compiler Version: v0.8.23+commit.f704f362 + +Deployed Contracts: +Hub: 0x7bC1F123089Bc1f384b6379d0587968d1CD5830a +Migration: 0xEaBa6046103C3A2f5A681fD4323f78C647Fb4292 +NameRegistry: 0xb95eF3f3E693531d9588815bcA954dC8dce30937 +ERC20Lift: 0xBB62B89fd96f42fe60216DCA3afefC67c95486d7 +StandardTreasury: 0x2434151eB40Af648AbcF73a6C9F1711FfF0F498B +BaseGroupMintPolicy: 0x2470B43fc3303fCa660E68c86e3bEb8CE353C556 +MastercopyDemurrageERC20: 0x3148182009D825aBfABfaeb11fc8F2e8a25dAc92 +MastercopyInflationaryERC20: 0x1b2fe34fCF2956A316936bC3F4E963fdaf9c1c72 +MastercopyStandardVault: 0x120DaC440c97d2D279988f056CCEf839B7E30409 diff --git a/script/deployments/package.json b/script/deployments/package.json index 94329c6..e9f010b 100644 --- a/script/deployments/package.json +++ b/script/deployments/package.json @@ -1,6 +1,6 @@ { "name": "deploy-circles", - "version": "0.3.4-alpha", + "version": "rc-0.3.5-alpha", "type": "module", "dependencies": { "dotenv": "^16.4.5", diff --git a/src/circles/Circles.sol b/src/circles/Circles.sol index baf4829..45d2c63 100644 --- a/src/circles/Circles.sol +++ b/src/circles/Circles.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.24; +import {ABDKMath64x64 as Math64x64} from "lib/abdk-libraries-solidity/ABDKMath64x64.sol"; import "../errors/Errors.sol"; -import "../lib/Math64x64.sol"; import "./ERC1155.sol"; contract Circles is ERC1155, ICirclesErrors { @@ -75,7 +75,7 @@ contract Circles is ERC1155, ICirclesErrors { DiscountedBalances(_inflation_day_zero) {} - // Public functions + // Internal functions /** * @notice Calculate the demurraged issuance for a human's avatar. @@ -84,8 +84,8 @@ contract Circles is ERC1155, ICirclesErrors { * @return startPeriod The start of the claimable period. * @return endPeriod The end of the claimable period. */ - function calculateIssuance(address _human) public view returns (uint256, uint256, uint256) { - MintTime storage mintTime = mintTimes[_human]; + function _calculateIssuance(address _human) internal view returns (uint256, uint256, uint256) { + MintTime memory mintTime = mintTimes[_human]; if (mintTime.mintV1Status != address(0) && mintTime.mintV1Status != CIRCLES_STOPPED_V1) { // Circles v1 contract cannot be active. revert CirclesERC1155MintBlocked(_human, mintTime.mintV1Status); @@ -128,73 +128,12 @@ contract Circles is ERC1155, ICirclesErrors { ); } - /** - * Inflationary balance of an account for a Circles identifier. Careful, - * calculating the inflationary balance can introduce numerical errors - * in the least significant digits (order of few attoCircles). - * @param _account Address for which the balance is queried. - * @param _id Circles identifier for which the balance is queried. - */ - function inflationaryBalanceOf(address _account, uint256 _id) public view returns (uint256) { - return _inflationaryBalanceOf(_account, _id); - } - - /** - * @notice safeInflationaryTransferFrom transfers Circles from one address to another by specifying inflationary units. - * @param _from Address from which the Circles are transferred. - * @param _to Address to which the Circles are transferred. - * @param _id Circles indentifier for which the Circles are transferred. - * @param _inflationaryValue Inflationary value of the Circles transferred. - * @param _data Data to pass to the receiver. - */ - function safeInflationaryTransferFrom( - address _from, - address _to, - uint256 _id, - uint256 _inflationaryValue, - bytes memory _data - ) public { - address sender = _msgSender(); - if (_from != sender && !isApprovedForAll(_from, sender)) { - revert ERC1155MissingApprovalForAll(sender, _from); - } - // convert inflationary value to todays demurrage value - uint256 value = convertInflationaryToDemurrageValue(_inflationaryValue, day(block.timestamp)); - _safeTransferFrom(_from, _to, _id, value, _data); - } - - /** - * @notice safeInflationaryBatchTransferFrom transfers Circles from one address to another by specifying inflationary units. - * @param _from Address from which the Circles are transferred. - * @param _to Address to which the Circles are transferred. - * @param _ids Batch of Circles identifiers for which the Circles are transferred. - * @param _inflationaryValues Batch of inflationary values of the Circles transferred. - * @param _data Data to pass to the receiver. - */ - function safeInflationaryBatchTransferFrom( - address _from, - address _to, - uint256[] memory _ids, - uint256[] memory _inflationaryValues, - bytes memory _data - ) public { - address sender = _msgSender(); - if (_from != sender && !isApprovedForAll(_from, sender)) { - revert ERC1155MissingApprovalForAll(sender, _from); - } - uint64 today = day(block.timestamp); - uint256[] memory values = convertBatchInflationaryToDemurrageValues(_inflationaryValues, today); - _safeBatchTransferFrom(_from, _to, _ids, values, _data); - } - - // Internal functions - /** * @notice Claim issuance for a human's avatar and update the last mint time. * @param _human Address of the human's avatar to claim the issuance for. */ function _claimIssuance(address _human) internal { - (uint256 issuance, uint256 startPeriod, uint256 endPeriod) = calculateIssuance(_human); + (uint256 issuance, uint256 startPeriod, uint256 endPeriod) = _calculateIssuance(_human); if (issuance == 0) { // No issuance to claim, simply return without reverting return; @@ -211,7 +150,7 @@ contract Circles is ERC1155, ICirclesErrors { _mint(_account, _id, _value, _data); uint64 today = day(block.timestamp); - DiscountedBalance storage totalSupplyBalance = discountedTotalSupplies[_id]; + DiscountedBalance memory totalSupplyBalance = discountedTotalSupplies[_id]; uint256 newTotalSupply = _calculateDiscountedBalance(totalSupplyBalance.balance, today - totalSupplyBalance.lastUpdatedDay) + _value; if (newTotalSupply > MAX_VALUE) { @@ -220,6 +159,7 @@ contract Circles is ERC1155, ICirclesErrors { } totalSupplyBalance.balance = uint192(newTotalSupply); totalSupplyBalance.lastUpdatedDay = today; + discountedTotalSupplies[_id] = totalSupplyBalance; } function _burnAndUpdateTotalSupply(address _account, uint256 _id, uint256 _value) internal { @@ -227,7 +167,7 @@ contract Circles is ERC1155, ICirclesErrors { _burn(_account, _id, _value); uint64 today = day(block.timestamp); - DiscountedBalance storage totalSupplyBalance = discountedTotalSupplies[_id]; + DiscountedBalance memory totalSupplyBalance = discountedTotalSupplies[_id]; uint256 discountedTotalSupply = _calculateDiscountedBalance(totalSupplyBalance.balance, today - totalSupplyBalance.lastUpdatedDay); if (discountedTotalSupply < _value) { @@ -242,6 +182,7 @@ contract Circles is ERC1155, ICirclesErrors { totalSupplyBalance.balance = uint192(discountedTotalSupply - _value); } totalSupplyBalance.lastUpdatedDay = today; + discountedTotalSupplies[_id] = totalSupplyBalance; } // Private functions diff --git a/src/circles/Demurrage.sol b/src/circles/Demurrage.sol index 98fd48a..76dd014 100644 --- a/src/circles/Demurrage.sol +++ b/src/circles/Demurrage.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.24; +import {ABDKMath64x64 as Math64x64} from "lib/abdk-libraries-solidity/ABDKMath64x64.sol"; import "../errors/Errors.sol"; -import "../lib/Math64x64.sol"; contract Demurrage is ICirclesDemurrageErrors { // Type declarations @@ -142,10 +142,6 @@ contract Demurrage is ICirclesDemurrageErrors { */ int128[15] internal R; - // Events - - event DiscountCost(address indexed account, uint256 indexed id, uint256 discountCost); - // Constructor constructor() { @@ -242,22 +238,27 @@ contract Demurrage is ICirclesDemurrageErrors { } function _calculateDemurrageFactor(uint256 _dayDifference) internal view returns (int128) { - if (_dayDifference <= R_TABLE_LOOKUP && R[_dayDifference] != 0) { - return R[_dayDifference]; - } else { - return Math64x64.pow(GAMMA_64x64, _dayDifference); + if (_dayDifference <= R_TABLE_LOOKUP) { + // if the day difference is in the lookup table, return the value from the table + int128 demurrageFactor = R[_dayDifference]; + if (demurrageFactor != 0) { + return demurrageFactor; + } } + // if the day difference is not in the lookup table, calculate the value + return Math64x64.pow(GAMMA_64x64, _dayDifference); } function _calculateDemurrageFactorAndCache(uint256 _dayDifference) internal returns (int128) { if (_dayDifference <= R_TABLE_LOOKUP) { - if (R[_dayDifference] == 0) { + int128 demurrageFactor = R[_dayDifference]; + if (demurrageFactor == 0) { // for proxy ERC20 contracts, the storage does not contain the R table yet // so compute it lazily and store it in the table - int128 r = Math64x64.pow(GAMMA_64x64, _dayDifference); - R[_dayDifference] = r; + demurrageFactor = Math64x64.pow(GAMMA_64x64, _dayDifference); + R[_dayDifference] = demurrageFactor; } - return R[_dayDifference]; + return demurrageFactor; } else { return Math64x64.pow(GAMMA_64x64, _dayDifference); } diff --git a/src/circles/DiscountedBalances.sol b/src/circles/DiscountedBalances.sol index 47a6636..5e81805 100644 --- a/src/circles/DiscountedBalances.sol +++ b/src/circles/DiscountedBalances.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.24; -import "../lib/Math64x64.sol"; +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import "./Demurrage.sol"; contract DiscountedBalances is Demurrage { @@ -95,9 +95,10 @@ contract DiscountedBalances is Demurrage { // DiscountedBalances: balance exceeds maximum value revert CirclesDemurrageAmountExceedsMaxUint190(_account, _id, _balance, 0); } - DiscountedBalance storage discountedBalance = discountedBalances[_id][_account]; + DiscountedBalance memory discountedBalance = discountedBalances[_id][_account]; discountedBalance.balance = uint192(_balance); discountedBalance.lastUpdatedDay = _day; + discountedBalances[_id][_account] = discountedBalance; } /** @@ -108,7 +109,7 @@ contract DiscountedBalances is Demurrage { * @param _day Day since inflation_day_zero to discount the balance to */ function _discountAndAddToBalance(address _account, uint256 _id, uint256 _value, uint64 _day) internal { - DiscountedBalance storage discountedBalance = discountedBalances[_id][_account]; + DiscountedBalance memory discountedBalance = discountedBalances[_id][_account]; if (_day < discountedBalance.lastUpdatedDay) { // DiscountedBalances: day is before last updated day revert CirclesDemurrageDayBeforeLastUpdatedDay(_account, _id, _day, discountedBalance.lastUpdatedDay, 1); @@ -122,7 +123,7 @@ contract DiscountedBalances is Demurrage { unchecked { uint256 discountCost = discountedBalance.balance - discountedBalanceOnDay; if (discountCost > 0) { - emit DiscountCost(_account, _id, discountCost); + emit IERC1155.TransferSingle(msg.sender, _account, address(0), _id, discountCost); } } uint256 updatedBalance = discountedBalanceOnDay + _value; @@ -132,5 +133,6 @@ contract DiscountedBalances is Demurrage { } discountedBalance.balance = uint192(updatedBalance); discountedBalance.lastUpdatedDay = _day; + discountedBalances[_id][_account] = discountedBalance; } } diff --git a/src/circles/ERC1155.sol b/src/circles/ERC1155.sol index 2c4a8af..8ad5f21 100644 --- a/src/circles/ERC1155.sol +++ b/src/circles/ERC1155.sol @@ -173,7 +173,7 @@ abstract contract ERC1155 is DiscountedBalances, Context, ERC165, IERC1155, IERC revert ERC1155InsufficientBalance(from, fromBalance, value, id); } if (discountCost > 0) { - emit DiscountCost(from, id, discountCost); + emit TransferSingle(operator, from, address(0), id, discountCost); } unchecked { // Overflow not possible: value <= fromBalance diff --git a/src/circles/InflationaryOperator.sol b/src/circles/InflationaryOperator.sol new file mode 100644 index 0000000..7097679 --- /dev/null +++ b/src/circles/InflationaryOperator.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.24; + +import "../hub/IHub.sol"; +import "./Demurrage.sol"; + +contract InflationaryCirclesOperator is Demurrage { + // Storage + + IHubV2 public hub; + + // Errors + + error InflationaryCirclesOperatorOnlyActOnBalancesOfSender(address sender, address from); + + // Modifier + + modifier OnlyActOnBalancesOfSender(address _from) { + if (_from != msg.sender) { + // only accept requests that act on the balances of msg.sender + revert InflationaryCirclesOperatorOnlyActOnBalancesOfSender(msg.sender, _from); + } + _; + } + + // Constructor + + constructor(IHubV2 _hub) { + hub = _hub; + } + + // Public functions + + /** + * Inflationary balance of an account for a Circles identifier. Careful, + * calculating the inflationary balance can introduce numerical errors + * in the least significant digits (order of few attoCircles). + * @param _account Address for which the balance is queried. + * @param _id Circles identifier for which the balance is queried. + */ + function inflationaryBalanceOf(address _account, uint256 _id) public view returns (uint256) { + return _inflationaryBalanceOf(_account, _id); + } + + /** + * @notice safeInflationaryTransferFrom transfers Circles from one address to another by specifying inflationary units. + * @param _from Address from which the Circles are transferred. + * @param _to Address to which the Circles are transferred. + * @param _id Circles indentifier for which the Circles are transferred. + * @param _inflationaryValue Inflationary value of the Circles transferred. + * @param _data Data to pass to the receiver. + */ + function safeInflationaryTransferFrom( + address _from, + address _to, + uint256 _id, + uint256 _inflationaryValue, + bytes memory _data + ) public OnlyActOnBalancesOfSender(_from) { + // convert inflationary value to todays demurrage value + uint256 value = convertInflationaryToDemurrageValue(_inflationaryValue, day(block.timestamp)); + // if from has this operator authorized, it can call ERC1155:safeTransferFrom + hub.safeTransferFrom(_from, _to, _id, value, _data); + } + + /** + * @notice safeInflationaryBatchTransferFrom transfers Circles from one address to another by specifying inflationary units. + * @param _from Address from which the Circles are transferred. + * @param _to Address to which the Circles are transferred. + * @param _ids Batch of Circles identifiers for which the Circles are transferred. + * @param _inflationaryValues Batch of inflationary values of the Circles transferred. + * @param _data Data to pass to the receiver. + */ + function safeInflationaryBatchTransferFrom( + address _from, + address _to, + uint256[] memory _ids, + uint256[] memory _inflationaryValues, + bytes memory _data + ) public OnlyActOnBalancesOfSender(_from) { + uint256[] memory values = convertBatchInflationaryToDemurrageValues(_inflationaryValues, day(block.timestamp)); + hub.safeBatchTransferFrom(_from, _to, _ids, values, _data); + } + + // Internal functions + + /** + * @dev Calculate the inflationary balance of a discounted balance + * @param _account Address of the account to calculate the balance of + * @param _id Circles identifier for which to calculate the balance + */ + function _inflationaryBalanceOf(address _account, uint256 _id) internal view returns (uint256) { + // retrieve the balance in demurrage units (of today) + uint256 balance = hub.balanceOf(_account, _id); + return _calculateInflationaryBalance(balance, day(block.timestamp)); + } +} diff --git a/src/errors/Errors.sol b/src/errors/Errors.sol index 9894daf..ffd15b3 100644 --- a/src/errors/Errors.sol +++ b/src/errors/Errors.sol @@ -24,8 +24,6 @@ interface IHubErrors { error CirclesHubFlowEdgeIsNotPermitted(address receiver, uint256 circlesId, uint8 code); - error CirclesHubOnClosedPathOnlyPersonalCirclesCanReturnToAvatar(address failedReceiver, uint256 circlesId); - error CirclesHubFlowVerticesMustBeSorted(); error CirclesHubFlowEdgeStreamMismatch(uint16 flowEdgeId, uint16 streamId, uint8 code); diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index 00bcd45..b0ce135 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -39,21 +39,19 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { */ address private constant SENTINEL = address(0x1); - bytes32 private constant ADVANCED_FLAG_OPTOUT_CONSENTEDFLOW = bytes32(uint256(1)); - // State variables - // /** - // * @notice The global name of Circles. - // * todo, change this to "Circles" for the production deployment - // */ - // string public name = "Rings"; + /** + * @notice The global name of Circles. + * todo, change this to "Circles" for the production deployment + */ + string public name = "Rings"; - // /** - // * @notice The global symbol ticker for Circles. - // * todo, change this to "CRC" for the production deployment - // */ - // string public symbol = "RING"; + /** + * @notice The global symbol ticker for Circles. + * todo, change this to "CRC" for the production deployment + */ + string public symbol = "RING"; /** * @notice The Hub v1 contract address. @@ -107,12 +105,6 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { */ mapping(address => address) public treasuries; - /** - * @notice By default the advanced usage flags should remain set to zero. - * Only for advanced purposes people can consider enabling flags. - */ - mapping(address => bytes32) public advancedUsageFlags; - /** * @notice The iterable mapping of directional trust relations between avatars and * their expiry times. @@ -132,6 +124,10 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { event Stopped(address indexed avatar); + event StreamCompleted( + address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] amounts + ); + // Modifiers /** @@ -381,6 +377,22 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { _claimIssuance(msg.sender); } + /** + * @notice Calculate the demurraged issuance for a human's avatar. + * @param _human Address of the human's avatar to calculate the issuance for. + * @return issuance The issuance in attoCircles. + * @return startPeriod The start of the claimable period. + * @return endPeriod The end of the claimable period. + */ + function calculateIssuance(address _human) external view returns (uint256, uint256, uint256) { + if (!isHuman(_human)) { + // Only avatars registered as human can calculate issuance. + // If the avatar is not registered as human, return 0 issuance. + return (0, 0, 0); + } + return _calculateIssuance(_human); + } + /** * @notice Calculate issuance allows to calculate the issuance for a human avatar with a check * to update the v1 mint status if updated. @@ -393,7 +405,7 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { // check if v1 Circles is known to be stopped and update status _checkHumanV1CirclesStatus(_human); // calculate issuance for the human avatar, but don't mint - return calculateIssuance(_human); + return _calculateIssuance(_human); } /** @@ -401,7 +413,7 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { * @param _group address of the group avatar to mint Circles of * @param _collateralAvatars array of (personal or group) avatar addresses to be used as collateral * @param _amounts array of amounts of collateral to be used for minting - * @param _data (optional) additional data to be passed to the mint policy, treasury and minter + * @param _data (optional) additional data to be passed to the mint policy, treasury and minter (caller) */ function groupMint( address _group, @@ -413,7 +425,7 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { for (uint256 i = 0; i < _collateralAvatars.length; i++) { collateral[i] = toTokenId(_collateralAvatars[i]); } - _groupMint(msg.sender, _group, collateral, _amounts, _data); + _groupMint(msg.sender, msg.sender, _group, collateral, _amounts, _data); } /** @@ -546,13 +558,14 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { } } - // if no streams are provided, then only closed paths are allowed - bool closedPath = (_streams.length == 0); + // if no streams are provided, the streams will nett to zero for all vertices + // so to pass the acceptance checks, the flow matrix must also nett to zero + // which can be true if for all vertices the sum of incoming and outgoing flow is zero // verify the correctness of the flow matrix describing the path itself, // ie. well-definedness of the flow matrix itself, // check all entities are registered, and the trust relations are respected. - int256[] memory matrixNettedFlow = _verifyFlowMatrix(_flowVertices, _flow, coordinates, closedPath); + int256[] memory matrixNettedFlow = _verifyFlowMatrix(_flowVertices, _flow, coordinates); _effectPathTransfers(_flowVertices, _flow, _streams, coordinates); @@ -561,14 +574,6 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { _matchNettedFlows(streamsNettedFlow, matrixNettedFlow); } - function setAdvancedUsageFlag(bytes32 _flag) external { - if (avatars[msg.sender] == address(0)) { - // Only registered avatars can set advanced usage flags. - revert CirclesAvatarMustBeRegistered(msg.sender, 3); - } - advancedUsageFlags[msg.sender] = _flag; - } - // Public functions /** @@ -606,15 +611,18 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { return uint256(trustMarkers[_truster][_trustee].expiry) >= block.timestamp; } + /** + * @notice Returns true if the flow to the receiver is permitted. + * The receiver must trust the Circles being sent, and the Circles avatar associated with + * the Circles must trust the receiver. + * @param _to Address of the receiver + * @param _circlesAvatar Address of the Circles avatar of the Circles being sent + * @return permitted true if the flow is permitted, false otherwise + */ function isPermittedFlow(address _to, address _circlesAvatar) public view returns (bool) { - // if receiver does not trust the Circles being sent, then the flow is not consented regardless + // if receiver does not trust the Circles being sent, then the flow is not permitted regardless if (uint256(trustMarkers[_to][_circlesAvatar].expiry) < block.timestamp) return false; - // if the advanced usage flag is set to opt-out of consented flow, - // then the uni-directional trust is sufficient - if (advancedUsageFlags[_circlesAvatar] & ADVANCED_FLAG_OPTOUT_CONSENTEDFLOW != bytes32(0)) { - return true; - } - // however, by default the consented flow requires bi-directional trust from center to receiver + // however, consented flow also requires bi-directional trust from center to receiver return uint256(trustMarkers[_circlesAvatar][_to].expiry) >= block.timestamp; } @@ -622,7 +630,8 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { /** * @notice Group mint allows to mint group Circles by providing the required collateral. - * @param _sender address of the sender of the group mint, and receiver of minted group Circles + * @param _sender address of the sender of the group mint + * @param _receiver address of the receiver of minted group Circles * @param _group address of the group avatar to mint Circles of * @param _collateral array of (personal or group) avatar addresses to be used as collateral * @param _amounts array of amounts of collateral to be used for minting @@ -630,6 +639,7 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { */ function _groupMint( address _sender, + address _receiver, address _group, uint256[] memory _collateral, uint256[] memory _amounts, @@ -655,9 +665,9 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { for (uint256 i = 0; i < _amounts.length; i++) { // _groupMint is only called from the public groupMint function, // or from operateFlowMatrix, and both ensure the collateral ids are derived - // from an address, so we can cast here without checks. + // from a registered address, so we can cast here without checking valid registration if (!isPermittedFlow(_group, _validateAddressFromId(_collateral[i], 2))) { - // Group does not trust collateral. + // Group does not trust collateral, or flow edge is not permitted revert CirclesHubFlowEdgeIsNotPermitted(_group, _collateral[i], 0); } @@ -683,15 +693,23 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { // note: treasury.on1155Received must implement and unpack the GroupMintMetadata to know the group safeBatchTransferFrom(_sender, treasuries[_group], _collateral, _amounts, dataWithGroup); - // mint group Circles to the sender and send the original _data onwards - _mintAndUpdateTotalSupply(_sender, toTokenId(_group), sumAmounts, _data); + // mint group Circles to the receiver and send the original _data onwards + _mintAndUpdateTotalSupply(_receiver, toTokenId(_group), sumAmounts, _data); } + /** + * @dev Verify the correctness of the flow matrix describing the path transfer + * @param _flowVertices an ordered list of avatar addresses as the vertices which the path touches + * @param _flow array of flow edges, each edge is a struct with the amount (uint192) + * and streamSinkId (reference to a stream, where for non-terminal flow edges this is 0, and for terminal flow edges + * this must reference the index of the stream in the streams array, starting from 1) + * @param _coordinates unpacked array of coordinates of the flow edges, with 3 coordinates per flow edge: + * Circles identifier being transfered, sender, receiver, each a uint16 referencing the flow vertex. + */ function _verifyFlowMatrix( address[] calldata _flowVertices, FlowEdge[] calldata _flow, - uint16[] memory _coordinates, - bool _closedPath + uint16[] memory _coordinates ) internal view returns (int256[] memory) { if (3 * _flow.length != _coordinates.length) { // Mismatch in flow and coordinates length. @@ -747,18 +765,11 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { // Flow edge is not permitted. revert CirclesHubFlowEdgeIsNotPermitted(to, toTokenId(circlesId), 1); } - if (_closedPath && (to != circlesId || isGroup(circlesId))) { - // Closed paths can only return personal Circles to source. - revert CirclesHubOnClosedPathOnlyPersonalCirclesCanReturnToAvatar(to, toTokenId(circlesId)); - } // nett the flow, dividing out the different Circle identifiers - // expect for all edges to a group, as they are interpreted - // as a request for group mint in _effectPathTransfers - if (!isGroup(to)) { - nettedFlow[_coordinates[index + 1]] -= flow; - nettedFlow[_coordinates[index + 2]] += flow; - } + nettedFlow[_coordinates[index + 1]] -= flow; + nettedFlow[_coordinates[index + 2]] += flow; + index = index + 3; } } @@ -766,6 +777,14 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { return nettedFlow; } + /** + * @dev Effect the flow edges of the path transfer, this will revert if any balance is insufficient + * @param _flowVertices an ordered list of avatar addresses as the vertices which the path touches + * @param _flow array of flow edges, each edge is a struct with the amount and streamSinkId + * @param _streams array of streams, each stream is a struct that references the source vertex coordinate, + * the ids of the terminal flow edges of this stream, and the data that is passed to the ERC1155 acceptance check + * @param _coordinates unpacked array of coordinates of the flow edges + */ function _effectPathTransfers( address[] calldata _flowVertices, FlowEdge[] calldata _flow, @@ -820,13 +839,14 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { amounts ); } else { - // do group mint, and the sender receives the minted group Circles + // do group mint, and the group itself receives the minted group Circles _groupMint( _flowVertices[_coordinates[index + 1]], // sender, from coordinate - to, // group + to, // receiver, to coordinate + to, // group; for triggering group mint, to == the group to mint for ids, // collateral amounts, // amounts - "" + "" // path-based group mints never send data to the mint policy ); } @@ -847,13 +867,20 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { } } + /** + * @dev Call the acceptance checks for the streams, and return the netted streams + * @param _flowVertices sorted array of avatar addresses as the vertices which the path touches + * @param _flow array of flow edges + * @param _streams array of streams + * @param _coordinates unpacked array of coordinates of the flow edges + */ function _callAcceptanceChecks( address[] calldata _flowVertices, FlowEdge[] calldata _flow, Stream[] calldata _streams, uint16[] memory _coordinates ) internal returns (int256[] memory) { - // initialize netted flow + // initialize netted flow to zero int256[] memory nettedFlow = new int256[](_flowVertices.length); // effect the stream transfers with acceptance calls @@ -881,6 +908,10 @@ contract Hub is Circles, TypeDefinitions, IHubErrors { nettedFlow[_streams[i].sourceCoordinate] -= int256(streamTotal); // to recover the receiver coordinate, get the first sink nettedFlow[receiverCoordinate] += int256(streamTotal); + + // emit the stream completed event which expresses the effective "ERC1155:BatchTransfer" event + // for the stream as part of a batch of path transfers. + emit StreamCompleted(msg.sender, _flowVertices[_streams[i].sourceCoordinate], receiver, ids, amounts); } return nettedFlow; diff --git a/src/lib/Math64x64.sol b/src/lib/Math64x64.sol deleted file mode 100644 index 08c23ed..0000000 --- a/src/lib/Math64x64.sol +++ /dev/null @@ -1,885 +0,0 @@ -// SPDX-License-Identifier: BSD-4-Clause -// solhint-disable -/* - * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting. - * Author: Mikhail Vladimirov - * - * from https://github.com/abdk-consulting/abdk-libraries-solidity - */ -pragma solidity ^0.8.4; - -/** - * Smart contract library of mathematical functions operating with signed - * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is - * basically a simple fraction whose numerator is signed 128-bit integer and - * denominator is 2^64. As long as denominator is always the same, there is no - * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are - * represented by int128 type holding only the numerator. - */ -library Math64x64 { - /* - * Minimum value signed 64.64-bit fixed point number may have. - */ - int128 private constant MIN_64x64 = -0x80000000000000000000000000000000; - - /* - * Maximum value signed 64.64-bit fixed point number may have. - */ - int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; - - /** - * Convert signed 256-bit integer number into signed 64.64-bit fixed point - * number. Revert on overflow. - * - * @param x signed 256-bit integer number - * @return signed 64.64-bit fixed point number - */ - function fromInt(int256 x) internal pure returns (int128) { - unchecked { - require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF); - return int128(x << 64); - } - } - - /** - * Convert signed 64.64 fixed point number into signed 64-bit integer number - * rounding down. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64-bit integer number - */ - function toInt(int128 x) internal pure returns (int64) { - unchecked { - return int64(x >> 64); - } - } - - /** - * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point - * number. Revert on overflow. - * - * @param x unsigned 256-bit integer number - * @return signed 64.64-bit fixed point number - */ - function fromUInt(uint256 x) internal pure returns (int128) { - unchecked { - require(x <= 0x7FFFFFFFFFFFFFFF); - return int128(int256(x << 64)); - } - } - - /** - * Convert signed 64.64 fixed point number into unsigned 64-bit integer - * number rounding down. Revert on underflow. - * - * @param x signed 64.64-bit fixed point number - * @return unsigned 64-bit integer number - */ - function toUInt(int128 x) internal pure returns (uint64) { - unchecked { - require(x >= 0); - return uint64(uint128(x >> 64)); - } - } - - /** - * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point - * number rounding down. Revert on overflow. - * - * @param x signed 128.128-bin fixed point number - * @return signed 64.64-bit fixed point number - */ - function from128x128(int256 x) internal pure returns (int128) { - unchecked { - int256 result = x >> 64; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Convert signed 64.64 fixed point number into signed 128.128 fixed point - * number. - * - * @param x signed 64.64-bit fixed point number - * @return signed 128.128 fixed point number - */ - function to128x128(int128 x) internal pure returns (int256) { - unchecked { - return int256(x) << 64; - } - } - - /** - * Calculate x + y. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function add(int128 x, int128 y) internal pure returns (int128) { - unchecked { - int256 result = int256(x) + y; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate x - y. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function sub(int128 x, int128 y) internal pure returns (int128) { - unchecked { - int256 result = int256(x) - y; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate x * y rounding down. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function mul(int128 x, int128 y) internal pure returns (int128) { - unchecked { - int256 result = int256(x) * y >> 64; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point - * number and y is signed 256-bit integer number. Revert on overflow. - * - * @param x signed 64.64 fixed point number - * @param y signed 256-bit integer number - * @return signed 256-bit integer number - */ - function muli(int128 x, int256 y) internal pure returns (int256) { - unchecked { - if (x == MIN_64x64) { - require( - y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - && y <= 0x1000000000000000000000000000000000000000000000000 - ); - return -y << 63; - } else { - bool negativeResult = false; - if (x < 0) { - x = -x; - negativeResult = true; - } - if (y < 0) { - y = -y; // We rely on overflow behavior here - negativeResult = !negativeResult; - } - uint256 absoluteResult = mulu(x, uint256(y)); - if (negativeResult) { - require(absoluteResult <= 0x8000000000000000000000000000000000000000000000000000000000000000); - return -int256(absoluteResult); // We rely on overflow behavior here - } else { - require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - return int256(absoluteResult); - } - } - } - } - - /** - * Calculate x * y rounding down, where x is signed 64.64 fixed point number - * and y is unsigned 256-bit integer number. Revert on overflow. - * - * @param x signed 64.64 fixed point number - * @param y unsigned 256-bit integer number - * @return unsigned 256-bit integer number - */ - function mulu(int128 x, uint256 y) internal pure returns (uint256) { - unchecked { - if (y == 0) return 0; - - require(x >= 0); - - uint256 lo = (uint256(int256(x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64; - uint256 hi = uint256(int256(x)) * (y >> 128); - - require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - hi <<= 64; - - require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo); - return hi + lo; - } - } - - /** - * Calculate x / y rounding towards zero. Revert on overflow or when y is - * zero. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function div(int128 x, int128 y) internal pure returns (int128) { - unchecked { - require(y != 0); - int256 result = (int256(x) << 64) / y; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate x / y rounding towards zero, where x and y are signed 256-bit - * integer numbers. Revert on overflow or when y is zero. - * - * @param x signed 256-bit integer number - * @param y signed 256-bit integer number - * @return signed 64.64-bit fixed point number - */ - function divi(int256 x, int256 y) internal pure returns (int128) { - unchecked { - require(y != 0); - - bool negativeResult = false; - if (x < 0) { - x = -x; // We rely on overflow behavior here - negativeResult = true; - } - if (y < 0) { - y = -y; // We rely on overflow behavior here - negativeResult = !negativeResult; - } - uint128 absoluteResult = divuu(uint256(x), uint256(y)); - if (negativeResult) { - require(absoluteResult <= 0x80000000000000000000000000000000); - return -int128(absoluteResult); // We rely on overflow behavior here - } else { - require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - return int128(absoluteResult); // We rely on overflow behavior here - } - } - } - - /** - * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit - * integer numbers. Revert on overflow or when y is zero. - * - * @param x unsigned 256-bit integer number - * @param y unsigned 256-bit integer number - * @return signed 64.64-bit fixed point number - */ - function divu(uint256 x, uint256 y) internal pure returns (int128) { - unchecked { - require(y != 0); - uint128 result = divuu(x, y); - require(result <= uint128(MAX_64x64)); - return int128(result); - } - } - - /** - * Calculate -x. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function neg(int128 x) internal pure returns (int128) { - unchecked { - require(x != MIN_64x64); - return -x; - } - } - - /** - * Calculate |x|. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function abs(int128 x) internal pure returns (int128) { - unchecked { - require(x != MIN_64x64); - return x < 0 ? -x : x; - } - } - - /** - * Calculate 1 / x rounding towards zero. Revert on overflow or when x is - * zero. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function inv(int128 x) internal pure returns (int128) { - unchecked { - require(x != 0); - int256 result = int256(0x100000000000000000000000000000000) / x; - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function avg(int128 x, int128 y) internal pure returns (int128) { - unchecked { - return int128((int256(x) + int256(y)) >> 1); - } - } - - /** - * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down. - * Revert on overflow or in case x * y is negative. - * - * @param x signed 64.64-bit fixed point number - * @param y signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function gavg(int128 x, int128 y) internal pure returns (int128) { - unchecked { - int256 m = int256(x) * int256(y); - require(m >= 0); - require(m < 0x4000000000000000000000000000000000000000000000000000000000000000); - return int128(sqrtu(uint256(m))); - } - } - - /** - * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number - * and y is unsigned 256-bit integer number. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @param y uint256 value - * @return signed 64.64-bit fixed point number - */ - function pow(int128 x, uint256 y) internal pure returns (int128) { - unchecked { - bool negative = x < 0 && y & 1 == 1; - - uint256 absX = uint128(x < 0 ? -x : x); - uint256 absResult; - absResult = 0x100000000000000000000000000000000; - - if (absX <= 0x10000000000000000) { - absX <<= 63; - while (y != 0) { - if (y & 0x1 != 0) { - absResult = absResult * absX >> 127; - } - absX = absX * absX >> 127; - - if (y & 0x2 != 0) { - absResult = absResult * absX >> 127; - } - absX = absX * absX >> 127; - - if (y & 0x4 != 0) { - absResult = absResult * absX >> 127; - } - absX = absX * absX >> 127; - - if (y & 0x8 != 0) { - absResult = absResult * absX >> 127; - } - absX = absX * absX >> 127; - - y >>= 4; - } - - absResult >>= 64; - } else { - uint256 absXShift = 63; - if (absX < 0x1000000000000000000000000) { - absX <<= 32; - absXShift -= 32; - } - if (absX < 0x10000000000000000000000000000) { - absX <<= 16; - absXShift -= 16; - } - if (absX < 0x1000000000000000000000000000000) { - absX <<= 8; - absXShift -= 8; - } - if (absX < 0x10000000000000000000000000000000) { - absX <<= 4; - absXShift -= 4; - } - if (absX < 0x40000000000000000000000000000000) { - absX <<= 2; - absXShift -= 2; - } - if (absX < 0x80000000000000000000000000000000) { - absX <<= 1; - absXShift -= 1; - } - - uint256 resultShift = 0; - while (y != 0) { - require(absXShift < 64); - - if (y & 0x1 != 0) { - absResult = absResult * absX >> 127; - resultShift += absXShift; - if (absResult > 0x100000000000000000000000000000000) { - absResult >>= 1; - resultShift += 1; - } - } - absX = absX * absX >> 127; - absXShift <<= 1; - if (absX >= 0x100000000000000000000000000000000) { - absX >>= 1; - absXShift += 1; - } - - y >>= 1; - } - - require(resultShift < 64); - absResult >>= 64 - resultShift; - } - int256 result = negative ? -int256(absResult) : int256(absResult); - require(result >= MIN_64x64 && result <= MAX_64x64); - return int128(result); - } - } - - /** - * Calculate sqrt (x) rounding down. Revert if x < 0. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function sqrt(int128 x) internal pure returns (int128) { - unchecked { - require(x >= 0); - return int128(sqrtu(uint256(int256(x)) << 64)); - } - } - - /** - * Calculate binary logarithm of x. Revert if x <= 0. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function log_2(int128 x) internal pure returns (int128) { - unchecked { - require(x > 0); - - int256 msb = 0; - int256 xc = x; - if (xc >= 0x10000000000000000) { - xc >>= 64; - msb += 64; - } - if (xc >= 0x100000000) { - xc >>= 32; - msb += 32; - } - if (xc >= 0x10000) { - xc >>= 16; - msb += 16; - } - if (xc >= 0x100) { - xc >>= 8; - msb += 8; - } - if (xc >= 0x10) { - xc >>= 4; - msb += 4; - } - if (xc >= 0x4) { - xc >>= 2; - msb += 2; - } - if (xc >= 0x2) msb += 1; // No need to shift xc anymore - - int256 result = msb - 64 << 64; - uint256 ux = uint256(int256(x)) << uint256(127 - msb); - for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) { - ux *= ux; - uint256 b = ux >> 255; - ux >>= 127 + b; - result += bit * int256(b); - } - - return int128(result); - } - } - - /** - * Calculate natural logarithm of x. Revert if x <= 0. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function ln(int128 x) internal pure returns (int128) { - unchecked { - require(x > 0); - - return int128(int256(uint256(int256(log_2(x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128)); - } - } - - /** - * Calculate binary exponent of x. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function exp_2(int128 x) internal pure returns (int128) { - unchecked { - require(x < 0x400000000000000000); // Overflow - - if (x < -0x400000000000000000) return 0; // Underflow - - uint256 result = 0x80000000000000000000000000000000; - - if (x & 0x8000000000000000 > 0) { - result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128; - } - if (x & 0x4000000000000000 > 0) { - result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128; - } - if (x & 0x2000000000000000 > 0) { - result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128; - } - if (x & 0x1000000000000000 > 0) { - result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128; - } - if (x & 0x800000000000000 > 0) { - result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128; - } - if (x & 0x400000000000000 > 0) { - result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128; - } - if (x & 0x200000000000000 > 0) { - result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128; - } - if (x & 0x100000000000000 > 0) { - result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128; - } - if (x & 0x80000000000000 > 0) { - result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128; - } - if (x & 0x40000000000000 > 0) { - result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128; - } - if (x & 0x20000000000000 > 0) { - result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128; - } - if (x & 0x10000000000000 > 0) { - result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128; - } - if (x & 0x8000000000000 > 0) { - result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128; - } - if (x & 0x4000000000000 > 0) { - result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128; - } - if (x & 0x2000000000000 > 0) { - result = result * 0x1000162E525EE054754457D5995292026 >> 128; - } - if (x & 0x1000000000000 > 0) { - result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128; - } - if (x & 0x800000000000 > 0) { - result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128; - } - if (x & 0x400000000000 > 0) { - result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128; - } - if (x & 0x200000000000 > 0) { - result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128; - } - if (x & 0x100000000000 > 0) { - result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128; - } - if (x & 0x80000000000 > 0) { - result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128; - } - if (x & 0x40000000000 > 0) { - result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128; - } - if (x & 0x20000000000 > 0) { - result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128; - } - if (x & 0x10000000000 > 0) { - result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128; - } - if (x & 0x8000000000 > 0) { - result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128; - } - if (x & 0x4000000000 > 0) { - result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128; - } - if (x & 0x2000000000 > 0) { - result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128; - } - if (x & 0x1000000000 > 0) { - result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128; - } - if (x & 0x800000000 > 0) { - result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128; - } - if (x & 0x400000000 > 0) { - result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128; - } - if (x & 0x200000000 > 0) { - result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128; - } - if (x & 0x100000000 > 0) { - result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128; - } - if (x & 0x80000000 > 0) { - result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128; - } - if (x & 0x40000000 > 0) { - result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128; - } - if (x & 0x20000000 > 0) { - result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128; - } - if (x & 0x10000000 > 0) { - result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128; - } - if (x & 0x8000000 > 0) { - result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128; - } - if (x & 0x4000000 > 0) { - result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128; - } - if (x & 0x2000000 > 0) { - result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128; - } - if (x & 0x1000000 > 0) { - result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128; - } - if (x & 0x800000 > 0) { - result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128; - } - if (x & 0x400000 > 0) { - result = result * 0x100000000002C5C85FDF477B662B26945 >> 128; - } - if (x & 0x200000 > 0) { - result = result * 0x10000000000162E42FEFA3AE53369388C >> 128; - } - if (x & 0x100000 > 0) { - result = result * 0x100000000000B17217F7D1D351A389D40 >> 128; - } - if (x & 0x80000 > 0) { - result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128; - } - if (x & 0x40000 > 0) { - result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128; - } - if (x & 0x20000 > 0) { - result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128; - } - if (x & 0x10000 > 0) { - result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128; - } - if (x & 0x8000 > 0) { - result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128; - } - if (x & 0x4000 > 0) { - result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128; - } - if (x & 0x2000 > 0) { - result = result * 0x1000000000000162E42FEFA39F02B772C >> 128; - } - if (x & 0x1000 > 0) { - result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128; - } - if (x & 0x800 > 0) { - result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128; - } - if (x & 0x400 > 0) { - result = result * 0x100000000000002C5C85FDF473DEA871F >> 128; - } - if (x & 0x200 > 0) { - result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128; - } - if (x & 0x100 > 0) { - result = result * 0x100000000000000B17217F7D1CF79E949 >> 128; - } - if (x & 0x80 > 0) { - result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128; - } - if (x & 0x40 > 0) { - result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128; - } - if (x & 0x20 > 0) { - result = result * 0x100000000000000162E42FEFA39EF366F >> 128; - } - if (x & 0x10 > 0) { - result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128; - } - if (x & 0x8 > 0) { - result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128; - } - if (x & 0x4 > 0) { - result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128; - } - if (x & 0x2 > 0) { - result = result * 0x1000000000000000162E42FEFA39EF358 >> 128; - } - if (x & 0x1 > 0) { - result = result * 0x10000000000000000B17217F7D1CF79AB >> 128; - } - - result >>= uint256(int256(63 - (x >> 64))); - require(result <= uint256(int256(MAX_64x64))); - - return int128(int256(result)); - } - } - - /** - * Calculate natural exponent of x. Revert on overflow. - * - * @param x signed 64.64-bit fixed point number - * @return signed 64.64-bit fixed point number - */ - function exp(int128 x) internal pure returns (int128) { - unchecked { - require(x < 0x400000000000000000); // Overflow - - if (x < -0x400000000000000000) return 0; // Underflow - - return exp_2(int128(int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128)); - } - } - - /** - * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit - * integer numbers. Revert on overflow or when y is zero. - * - * @param x unsigned 256-bit integer number - * @param y unsigned 256-bit integer number - * @return unsigned 64.64-bit fixed point number - */ - function divuu(uint256 x, uint256 y) private pure returns (uint128) { - unchecked { - require(y != 0); - - uint256 result; - - if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) { - result = (x << 64) / y; - } else { - uint256 msb = 192; - uint256 xc = x >> 192; - if (xc >= 0x100000000) { - xc >>= 32; - msb += 32; - } - if (xc >= 0x10000) { - xc >>= 16; - msb += 16; - } - if (xc >= 0x100) { - xc >>= 8; - msb += 8; - } - if (xc >= 0x10) { - xc >>= 4; - msb += 4; - } - if (xc >= 0x4) { - xc >>= 2; - msb += 2; - } - if (xc >= 0x2) msb += 1; // No need to shift xc anymore - - result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1); - require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - uint256 hi = result * (y >> 128); - uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - uint256 xh = x >> 192; - uint256 xl = x << 64; - - if (xl < lo) xh -= 1; - xl -= lo; // We rely on overflow behavior here - lo = hi << 128; - if (xl < lo) xh -= 1; - xl -= lo; // We rely on overflow behavior here - - assert(xh == hi >> 128); - - result += xl / y; - } - - require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - return uint128(result); - } - } - - /** - * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer - * number. - * - * @param x unsigned 256-bit integer number - * @return unsigned 128-bit integer number - */ - function sqrtu(uint256 x) private pure returns (uint128) { - unchecked { - if (x == 0) { - return 0; - } else { - uint256 xx = x; - uint256 r = 1; - if (xx >= 0x100000000000000000000000000000000) { - xx >>= 128; - r <<= 64; - } - if (xx >= 0x10000000000000000) { - xx >>= 64; - r <<= 32; - } - if (xx >= 0x100000000) { - xx >>= 32; - r <<= 16; - } - if (xx >= 0x10000) { - xx >>= 16; - r <<= 8; - } - if (xx >= 0x100) { - xx >>= 8; - r <<= 4; - } - if (xx >= 0x10) { - xx >>= 4; - r <<= 2; - } - if (xx >= 0x8) r <<= 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; // Seven iterations should be enough - uint256 r1 = x / r; - return uint128(r < r1 ? r : r1); - } - } - } -} diff --git a/src/lift/ERC20DiscountedBalances.sol b/src/lift/ERC20DiscountedBalances.sol index 35c1011..139d406 100644 --- a/src/lift/ERC20DiscountedBalances.sol +++ b/src/lift/ERC20DiscountedBalances.sol @@ -107,13 +107,14 @@ contract ERC20DiscountedBalances is ERC20Permit, Demurrage, IERC20 { // Balance exceeds maximum value. revert CirclesDemurrageAmountExceedsMaxUint190(_account, toTokenId(avatar), _balance, 0); } - DiscountedBalance storage discountedBalance = discountedBalances[_account]; + DiscountedBalance memory discountedBalance = discountedBalances[_account]; discountedBalance.balance = uint192(_balance); discountedBalance.lastUpdatedDay = _day; + discountedBalances[_account] = discountedBalance; } function _discountAndAddToBalance(address _account, uint256 _value, uint64 _day) internal { - DiscountedBalance storage discountedBalance = discountedBalances[_account]; + DiscountedBalance memory discountedBalance = discountedBalances[_account]; if (_day < discountedBalance.lastUpdatedDay) { // ERC20 DiscountedBalances: day is before last updated day revert CirclesDemurrageDayBeforeLastUpdatedDay( @@ -128,7 +129,7 @@ contract ERC20DiscountedBalances is ERC20Permit, Demurrage, IERC20 { unchecked { uint256 discountCost = discountedBalance.balance - discountedBalanceOnDay; if (discountCost > 0) { - emit DiscountCost(_account, toTokenId(avatar), discountCost); + emit Transfer(_account, address(0), discountCost); } } uint256 updatedBalance = discountedBalanceOnDay + _value; @@ -138,6 +139,7 @@ contract ERC20DiscountedBalances is ERC20Permit, Demurrage, IERC20 { } discountedBalance.balance = uint192(updatedBalance); discountedBalance.lastUpdatedDay = _day; + discountedBalances[_account] = discountedBalance; } function _transfer(address _from, address _to, uint256 _amount) internal { @@ -147,7 +149,7 @@ contract ERC20DiscountedBalances is ERC20Permit, Demurrage, IERC20 { revert ERC20InsufficientBalance(_from, fromBalance, _amount); } if (discountCost > 0) { - emit DiscountCost(_from, toTokenId(avatar), discountCost); + emit Transfer(_from, address(0), discountCost); } unchecked { _updateBalance(_from, fromBalance - _amount, day); @@ -169,7 +171,7 @@ contract ERC20DiscountedBalances is ERC20Permit, Demurrage, IERC20 { revert ERC20InsufficientBalance(_owner, ownerBalance, _amount); } if (discountCost > 0) { - emit DiscountCost(_owner, toTokenId(avatar), discountCost); + emit Transfer(_owner, address(0), discountCost); } unchecked { _updateBalance(_owner, ownerBalance - _amount, day); diff --git a/src/proxy/Proxy.sol b/src/proxy/Proxy.sol index 27f5fc9..2b4e61d 100755 --- a/src/proxy/Proxy.sol +++ b/src/proxy/Proxy.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only -// Adapted from https://github.com/gnosis/safe-contracts +// Adapted from https://github.com/gnosis/safe-contracts (originally) +// and updated with https://github.com/safe-global/safe-smart-account/blob/main/contracts/proxies/SafeProxy.sol pragma solidity >=0.8.4; -import "../errors/Errors.sol"; - /// @title IProxy - interface to access master copy of the proxy on-chain interface IProxy { function masterCopy() external view returns (address); @@ -14,49 +13,32 @@ interface IProxy { /// @author Stefan George - /// @author Richard Meissner - contract Proxy { - // masterCopy always needs to be first declared variable, - // to ensure that it is at the same location in the contracts - // to which calls are delegated. - // To reduce deployment costs this variable is internal - // and needs to be retrieved via `getStorageAt` - address public masterCopy; - - /// @dev Constructor function sets address of master copy contract. - /// @param _masterCopy Master copy address. - constructor(address _masterCopy) { - if (_masterCopy == address(0)) { - // Invalid master copy address provided - revert(); - } - masterCopy = _masterCopy; - } - - // todo: consider removing payable - fallback() external payable { - _fallback(); - } - - receive() external payable { - _fallback(); + // Singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated. + // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt` + address internal singleton; + + /** + * @notice Constructor function sets address of singleton contract. + * @param _singleton Singleton address. + */ + constructor(address _singleton) { + require(_singleton != address(0), "Invalid singleton address provided"); + singleton = _singleton; } - // -- internal functions - - /// @dev Fallback function forwards all transactions and - /// returns all received return data. - function _fallback() internal { + /// @dev Fallback function forwards all transactions and returns all received return data. + /// The fallback function is not payable because at no plpointace in Circles do these proxy contracts + /// need to receive xDAI (on Gnosis Chain, or Ether if they are deployed on Ethereum) + fallback() external { // solhint-disable-next-line no-inline-assembly assembly { - let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff) - // 0xa619486e == keccak("masterCopy()"). - // The value is right padded to 32-bytes with 0s - // solhint-disable-next-line max-line-length + let _singleton := sload(0) + // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) { - mstore(0, _singleton) + mstore(0, shr(12, shl(12, _singleton))) return(0, 0x20) } calldatacopy(0, 0, calldatasize()) - // solhint-disable-next-line max-line-length let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) if eq(success, 0) { revert(0, returndatasize()) } diff --git a/src/treasury/StandardTreasury.sol b/src/treasury/StandardTreasury.sol index a41ebff..3437ba5 100644 --- a/src/treasury/StandardTreasury.sol +++ b/src/treasury/StandardTreasury.sol @@ -47,6 +47,15 @@ contract StandardTreasury is */ mapping(address => IStandardVault) public vaults; + // Events + + event CreateVault(address indexed group, address indexed vault); + event GroupMintSingle(address indexed group, uint256 indexed id, uint256 value, bytes userData); + event GroupMintBatch(address indexed group, uint256[] ids, uint256[] values, bytes userData); + event GroupRedeem(address indexed group, uint256 indexed id, uint256 value, bytes data); + event GroupRedeemCollateralReturn(address indexed group, address indexed to, uint256[] ids, uint256[] values); + event GroupRedeemCollateralBurn(address indexed group, uint256[] ids, uint256[] values); + // Modifiers /** @@ -145,6 +154,10 @@ contract StandardTreasury is address vault = address(_ensureVault(_group)); // forward the Circles to the vault hub.safeBatchTransferFrom(address(this), vault, _ids, _values, _userData); + + // emit the group mint event + emit GroupMintBatch(_group, _ids, _values, _userData); + return this.onERC1155BatchReceived.selector; } @@ -156,6 +169,10 @@ contract StandardTreasury is address vault = address(_ensureVault(_group)); // forward the Circles to the vault hub.safeTransferFrom(address(this), vault, _id, _value, _userData); + + // emit the group mint event + emit GroupMintSingle(_group, _id, _value, _userData); + return this.onERC1155Received.selector; } @@ -209,6 +226,11 @@ contract StandardTreasury is // burn the collateral Circles from the vault vault.burnCollateral(burnIds, burnValues, _data); + // emit the group redeem event + emit GroupRedeem(group, _id, _value, _data); + emit GroupRedeemCollateralReturn(group, _from, redemptionIds, redemptionValues); + emit GroupRedeemCollateralBurn(group, burnIds, burnValues); + // return the ERC1155 selector for acceptance of the (redeemed) group Circles return this.onERC1155Received.selector; } @@ -259,6 +281,8 @@ contract StandardTreasury is if (address(vault) == address(0)) { vault = _deployVault(); vaults[_group] = vault; + + emit CreateVault(_group, address(vault)); } return vault; } diff --git a/test/circles/MockCircles.sol b/test/circles/MockCircles.sol index 8697f34..ce54199 100644 --- a/test/circles/MockCircles.sol +++ b/test/circles/MockCircles.sol @@ -25,4 +25,8 @@ contract MockCircles is Circles { require(mintTimes[msg.sender].lastMintTime != 0, "Circles: Not registered"); _claimIssuance(msg.sender); } + + function calculateIssuance(address _human) external view returns (uint256, uint256, uint256) { + return _calculateIssuance(_human); + } } diff --git a/test/lift/ERC20Demurrage.sol b/test/lift/ERC20Demurrage.sol index 4ec6292..3c0180b 100644 --- a/test/lift/ERC20Demurrage.sol +++ b/test/lift/ERC20Demurrage.sol @@ -65,7 +65,7 @@ contract ERC20LiftTest is Test, TimeCirclesSetup, HumanRegistration { function testERC20Demurrage() public { DemurrageCircles aliceERC20 = erc20s[addresses[0]]; // DemurrageCircles bobERC20 = erc20s[addresses[1]]; - uint256 aliceCirclesId = uint256(uint160(addresses[0])); + // uint256 aliceCirclesId = uint256(uint160(addresses[0])); // commenting out because stack too deep; todo write test cleaner @@ -87,8 +87,6 @@ contract ERC20LiftTest is Test, TimeCirclesSetup, HumanRegistration { // send 50 CRC from Alice to Bob vm.prank(addresses[0]); - vm.expectEmit(true, true, false, true, address(aliceERC20)); - emit Demurrage.DiscountCost(addresses[0], aliceCirclesId, aliceDiscountCostT2); aliceERC20.transfer(addresses[1], 50 * CRC); (uint256 aliceBalanceT3, uint256 aliceDiscountCostT3) = aliceERC20.balanceOfOnDay(addresses[0], dayT2); diff --git a/test/utils/Approximation.sol b/test/utils/Approximation.sol index ba444d7..dcfb090 100644 --- a/test/utils/Approximation.sol +++ b/test/utils/Approximation.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.13; -import "../../src/lib/Math64x64.sol"; +import {ABDKMath64x64 as Math64x64} from "lib/abdk-libraries-solidity/ABDKMath64x64.sol"; contract Approximation { // Constants