@coti-io/coti-contracts 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +18 -14
- package/README.md +3 -1
- package/contracts/disperse/coinByRatio/FixedRatioCoinDisperser.sol +452 -0
- package/contracts/disperse/coinByRatio/FixedRatioCoinDisperserLeftoverS1.sol +490 -0
- package/contracts/disperse/coinByRatio/FixedRatioCoinDisperserUnlimitedWindow.sol +432 -0
- package/contracts/disperse/disperseToken/MintDisperser.sol +120 -0
- package/contracts/disperse/disperseToken/TokenDisperser.sol +139 -0
- package/contracts/mocks/token/ERC20Mock.sol +19 -0
- package/contracts/mocks/token/PrivateERC20/EncryptedTokenReceiverMock.sol +18 -0
- package/contracts/mocks/token/PrivateERC20/PrivacyBridgeERC20Mock.sol +13 -0
- package/contracts/mocks/token/PrivateERC20/PrivateERC20AuditorSupplyMock.sol +44 -0
- package/contracts/mocks/token/PrivateERC20/PrivateERC20CappedMock.sol +23 -0
- package/contracts/mocks/token/PrivateERC20/PrivateERC20GtCallerMock.sol +29 -0
- package/contracts/mocks/token/PrivateERC20/PrivateERC20Mock.sol +9 -4
- package/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol +39 -0
- package/contracts/mocks/token/PrivateERC20/PublicReentrantTokenReceiverMock.sol +29 -0
- package/contracts/mocks/token/PrivateERC20/PublicTokenReceiverBoolMock.sol +18 -0
- package/contracts/mocks/token/PrivateERC20/PublicTokenReceiverMock.sol +12 -0
- package/contracts/mocks/utils/mpc/Arithmetic128TestsContract.sol +123 -0
- package/contracts/mocks/utils/mpc/Arithmetic256TestsContract.sol +122 -0
- package/contracts/mocks/utils/mpc/Bitwise128TestsContract.sol +65 -0
- package/contracts/mocks/utils/mpc/Bitwise256TestsContract.sol +65 -0
- package/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract.sol +64 -0
- package/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract.sol +64 -0
- package/contracts/mocks/utils/mpc/Comparison128TestsContract.sol +98 -0
- package/contracts/mocks/utils/mpc/Comparison256TestsContract.sol +98 -0
- package/contracts/mocks/utils/mpc/MinMax128TestsContract.sol +44 -0
- package/contracts/mocks/utils/mpc/MinMax256TestsContract.sol +44 -0
- package/contracts/mocks/utils/mpc/MinimalImplementation.sol +55 -0
- package/contracts/mocks/utils/mpc/MinimalProxy.sol +27 -0
- package/contracts/mocks/utils/mpc/MpcOperations128TestContract.sol +267 -0
- package/contracts/mocks/utils/mpc/MpcOperationsTestContract.sol +334 -0
- package/contracts/mocks/utils/mpc/Mux128TestsContract.sol +24 -0
- package/contracts/mocks/utils/mpc/Mux256TestsContract.sol +24 -0
- package/contracts/mocks/utils/mpc/OnBoard128TestsContract.sol +93 -0
- package/contracts/mocks/utils/mpc/OnBoard256TestsContract.sol +116 -0
- package/contracts/mocks/utils/mpc/PrivacyImplementationV1.sol +157 -0
- package/contracts/mocks/utils/mpc/PrivacyImplementationV2.sol +133 -0
- package/contracts/mocks/utils/mpc/PrivacyProxy.sol +137 -0
- package/contracts/mocks/utils/mpc/Random128TestsContract.sol +32 -0
- package/contracts/mocks/utils/mpc/Random256TestsContract.sol +32 -0
- package/contracts/mocks/utils/mpc/Shift128TestsContract.sol +36 -0
- package/contracts/mocks/utils/mpc/Shift256TestsContract.sol +36 -0
- package/contracts/mocks/utils/mpc/Transfer128TestsContract.sol +29 -0
- package/contracts/mocks/utils/mpc/Transfer256TestsContract.sol +29 -0
- package/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract.sol +32 -0
- package/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract.sol +32 -0
- package/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract.sol +59 -0
- package/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract.sol +63 -0
- package/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract.sol +99 -0
- package/contracts/mocks/wallet/PrivateERC20Wallet/PrivateERC20WalletMock.sol +7 -6
- package/contracts/node/CotiNodeRewards.sol +199 -0
- package/contracts/node/SoulboundNodeNFT.sol +519 -0
- package/contracts/privacyBridge/PrivacyBridge.sol +318 -0
- package/contracts/privacyBridge/PrivacyBridgeCotiNative.sol +268 -0
- package/contracts/privacyBridge/PrivacyBridgeERC20.sol +251 -0
- package/contracts/privacyBridge/PrivacyBridgeUSDCe.sol +16 -0
- package/contracts/privacyBridge/PrivacyBridgeUSDT.sol +17 -0
- package/contracts/privacyBridge/PrivacyBridgeWADA.sol +17 -0
- package/contracts/privacyBridge/PrivacyBridgeWBTC.sol +17 -0
- package/contracts/privacyBridge/PrivacyBridgeWETH.sol +17 -0
- package/contracts/privacyBridge/PrivacyBridgegCoti.sol +17 -0
- package/contracts/token/PrivateERC20/IPrivateERC20.sol +273 -40
- package/contracts/token/PrivateERC20/ITokenReceiver.sol +17 -0
- package/contracts/token/PrivateERC20/ITokenReceiverEncrypted.sol +20 -0
- package/contracts/token/PrivateERC20/PrivateERC20.sol +861 -152
- package/contracts/token/PrivateERC20/tokens/PrivateBridgedUSDC.sol +17 -0
- package/contracts/token/PrivateERC20/tokens/PrivateCOTI.sol +13 -0
- package/contracts/token/PrivateERC20/tokens/PrivateCOTITreasuryGovernanceToken.sol +17 -0
- package/contracts/token/PrivateERC20/tokens/PrivateTetherUSD.sol +17 -0
- package/contracts/token/PrivateERC20/tokens/PrivateWrappedADA.sol +17 -0
- package/contracts/token/PrivateERC20/tokens/PrivateWrappedBTC.sol +17 -0
- package/contracts/token/PrivateERC20/tokens/PrivateWrappedEther.sol +13 -0
- package/contracts/utils/mpc/MpcCore.sol +10780 -4962
- package/contracts/utils/mpc/MpcInterface.sol +7 -2
- package/hardhat/gasPriceBump.ts +60 -0
- package/hardhat.config.ts +60 -12
- package/package.json +21 -3
- package/scripts/deploySoulboundNodeNFT.ts +57 -0
- package/test/token/PrivateERC20/PrivacyBridge.fees.test.ts +327 -0
- package/test/token/PrivateERC20/PrivateERC20.test.ts +1109 -268
- package/test/utils/mpc/Bitwise128.test.ts +92 -0
- package/test/utils/mpc/Bitwise256.test.ts +101 -0
- package/test/utils/mpc/BuildInputText128.test.ts +398 -0
- package/test/utils/mpc/CheckedArithmetic128.test.ts +45 -0
- package/test/utils/mpc/CheckedArithmetic256.test.ts +45 -0
- package/test/utils/mpc/Comparison128.test.ts +48 -0
- package/test/utils/mpc/Comparison256.test.ts +48 -0
- package/test/utils/mpc/InvalidCiphertext.test.ts +179 -0
- package/test/utils/mpc/InvalidCiphertext128.test.ts +179 -0
- package/test/utils/mpc/InvalidCiphertext256.test.ts +201 -0
- package/test/utils/mpc/InvalidTransfer128.test.ts +166 -0
- package/test/utils/mpc/InvalidTransfer256.test.ts +158 -0
- package/test/utils/mpc/MinMax128.test.ts +44 -0
- package/test/utils/mpc/MinMax256.test.ts +44 -0
- package/test/utils/mpc/MinimalProxyDemo.test.ts +280 -0
- package/test/utils/mpc/MpcOperations.test.ts +589 -0
- package/test/utils/mpc/MpcOperations128.test.ts +632 -0
- package/test/utils/mpc/Mux128.test.ts +41 -0
- package/test/utils/mpc/Mux256.test.ts +41 -0
- package/test/utils/mpc/NegativeCases.test.ts +478 -0
- package/test/utils/mpc/NegativeCases128.test.ts +237 -0
- package/test/utils/mpc/NegativeCases256.test.ts +236 -0
- package/test/utils/mpc/OnBoard128.test.ts +90 -0
- package/test/utils/mpc/OnBoard256.test.ts +114 -0
- package/test/utils/mpc/Precompile128.test.ts +51 -0
- package/test/utils/mpc/Precompile256.test.ts +115 -0
- package/test/utils/mpc/ProxyValidation.test.ts +199 -0
- package/test/utils/mpc/Random128.test.ts +47 -0
- package/test/utils/mpc/Random256.test.ts +47 -0
- package/test/utils/mpc/Shift128.test.ts +41 -0
- package/test/utils/mpc/Shift256.test.ts +41 -0
- package/test/utils/mpc/Transfer128.test.ts +46 -0
- package/test/utils/mpc/Transfer256.test.ts +46 -0
- package/test/utils/mpc/TransferWithAllowance128.test.ts +48 -0
- package/test/utils/mpc/TransferWithAllowance256.test.ts +48 -0
- package/test/utils/mpc/UpgradeablePrivacyProxy.test.ts +307 -0
- package/test/utils/mpc/ValidateCiphertext.test.ts +221 -0
- package/test/utils/mpc/ValidateCiphertext256.test.ts +161 -0
- package/test/utils/mpc/ValidateCiphertext256Decryption.test.ts +383 -0
- package/test/utils/mpc/WalletEncryptionFailures.test.ts +494 -0
- package/test/utils/privateErc20Helpers.ts +97 -0
- package/typechain-types/@openzeppelin/contracts/access/Ownable.ts +153 -0
- package/typechain-types/@openzeppelin/contracts/access/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/index.ts +2 -0
- package/typechain-types/@openzeppelin/contracts/token/ERC20/IERC20.ts +262 -0
- package/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.ts +143 -0
- package/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/token/ERC20/index.ts +6 -0
- package/typechain-types/@openzeppelin/contracts/token/index.ts +2 -0
- package/typechain-types/contracts/disperse/coinByRatio/FixedRatioCoinDisperser.ts +679 -0
- package/typechain-types/contracts/disperse/coinByRatio/index.ts +4 -0
- package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/IMintableERC20.ts +97 -0
- package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/MintDisperser.ts +206 -0
- package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/index.ts +5 -0
- package/typechain-types/contracts/disperse/disperseToken/TokenDisperser.ts +191 -0
- package/typechain-types/contracts/disperse/disperseToken/index.ts +6 -0
- package/typechain-types/contracts/disperse/index.ts +7 -0
- package/typechain-types/contracts/index.ts +2 -0
- package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20Mock.ts +77 -60
- package/typechain-types/contracts/mocks/utils/mpc/Arithmetic128TestsContract.ts +311 -0
- package/typechain-types/contracts/mocks/utils/mpc/Arithmetic256TestsContract.ts +311 -0
- package/typechain-types/contracts/mocks/utils/mpc/Bitwise128TestsContract.ts +179 -0
- package/typechain-types/contracts/mocks/utils/mpc/Bitwise256TestsContract.ts +179 -0
- package/typechain-types/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract.ts +206 -0
- package/typechain-types/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract.ts +206 -0
- package/typechain-types/contracts/mocks/utils/mpc/Comparison128TestsContract.ts +278 -0
- package/typechain-types/contracts/mocks/utils/mpc/Comparison256TestsContract.ts +278 -0
- package/typechain-types/contracts/mocks/utils/mpc/MinMax128TestsContract.ts +142 -0
- package/typechain-types/contracts/mocks/utils/mpc/MinMax256TestsContract.ts +142 -0
- package/typechain-types/contracts/mocks/utils/mpc/MinimalImplementation.ts +259 -0
- package/typechain-types/contracts/mocks/utils/mpc/MinimalProxy.ts +90 -0
- package/typechain-types/contracts/mocks/utils/mpc/MpcOperations128TestContract.ts +388 -0
- package/typechain-types/contracts/mocks/utils/mpc/MpcOperationsTestContract.ts +455 -0
- package/typechain-types/contracts/mocks/utils/mpc/Mux128TestsContract.ts +109 -0
- package/typechain-types/contracts/mocks/utils/mpc/Mux256TestsContract.ts +109 -0
- package/typechain-types/contracts/mocks/utils/mpc/OnBoard128TestsContract.ts +166 -0
- package/typechain-types/contracts/mocks/utils/mpc/OnBoard256TestsContract.ts +194 -0
- package/typechain-types/contracts/mocks/utils/mpc/PrivacyImplementationV1.ts +385 -0
- package/typechain-types/contracts/mocks/utils/mpc/PrivacyImplementationV2.ts +441 -0
- package/typechain-types/contracts/mocks/utils/mpc/PrivacyProxy.ts +201 -0
- package/typechain-types/contracts/mocks/utils/mpc/Random128TestsContract.ts +131 -0
- package/typechain-types/contracts/mocks/utils/mpc/Random256TestsContract.ts +131 -0
- package/typechain-types/contracts/mocks/utils/mpc/Shift128TestsContract.ts +142 -0
- package/typechain-types/contracts/mocks/utils/mpc/Shift256TestsContract.ts +142 -0
- package/typechain-types/contracts/mocks/utils/mpc/Transfer128TestsContract.ts +109 -0
- package/typechain-types/contracts/mocks/utils/mpc/Transfer256TestsContract.ts +109 -0
- package/typechain-types/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract.ts +128 -0
- package/typechain-types/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract.ts +128 -0
- package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract.ts +198 -0
- package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract.ts +182 -0
- package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract.ts +257 -0
- package/typechain-types/contracts/mocks/utils/mpc/index.ts +32 -8
- package/typechain-types/contracts/token/PrivateERC20/IPrivateERC20.ts +77 -60
- package/typechain-types/contracts/token/PrivateERC20/PrivateERC20.ts +77 -60
- package/typechain-types/contracts/utils/mpc/MpcInterface.sol/ExtendedOperations.ts +111 -10
- package/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +74 -0
- package/typechain-types/factories/@openzeppelin/contracts/access/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/index.ts +1 -0
- package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/IERC20__factory.ts +205 -0
- package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit__factory.ts +100 -0
- package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/index.ts +5 -0
- package/typechain-types/factories/@openzeppelin/contracts/token/index.ts +1 -0
- package/typechain-types/factories/contracts/disperse/coinByRatio/FixedRatioCoinDisperser__factory.ts +759 -0
- package/typechain-types/factories/contracts/disperse/coinByRatio/index.ts +4 -0
- package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/IMintableERC20__factory.ts +43 -0
- package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/MintDisperser__factory.ts +185 -0
- package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/index.ts +5 -0
- package/typechain-types/factories/contracts/disperse/disperseToken/TokenDisperser__factory.ts +198 -0
- package/typechain-types/factories/contracts/disperse/disperseToken/index.ts +5 -0
- package/typechain-types/factories/contracts/disperse/index.ts +5 -0
- package/typechain-types/factories/contracts/index.ts +1 -0
- package/typechain-types/factories/contracts/mocks/access/DataPrivacyFramework/DataPrivacyFrameworkMock__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20Mock__factory.ts +179 -47
- package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic128TestsContract__factory.ts +330 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic256TestsContract__factory.ts +330 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/ArithmeticTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise128TestsContract__factory.ts +184 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise256TestsContract__factory.ts +184 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/BitwiseTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract__factory.ts +203 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract__factory.ts +203 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmeticWIthOverflowBitTestsContract.sol/CheckedArithmeticWithOverflowBitTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison128TestsContract__factory.ts +295 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison1TestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison256TestsContract__factory.ts +295 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison2TestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/MinMax128TestsContract__factory.ts +147 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/MinMax256TestsContract__factory.ts +147 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/MinMaxTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/MinimalImplementation__factory.ts +263 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/MinimalProxy__factory.ts +104 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous1TestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/MiscellaneousTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/MpcOperations128TestContract__factory.ts +723 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/MpcOperationsTestContract__factory.ts +1410 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Mux128TestsContract__factory.ts +115 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Mux256TestsContract__factory.ts +115 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/OffboardToUserKeyTestContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/OnBoard128TestsContract__factory.ts +175 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/OnBoard256TestsContract__factory.ts +205 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyImplementationV1__factory.ts +521 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyImplementationV2__factory.ts +648 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyProxy__factory.ts +179 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Random128TestsContract__factory.ts +131 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Random256TestsContract__factory.ts +131 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Shift128TestsContract__factory.ts +147 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Shift256TestsContract__factory.ts +147 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/ShiftTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/StringTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/Transfer128TestsContract__factory.ts +135 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/Transfer256TestsContract__factory.ts +135 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferScalarTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract__factory.ts +154 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract__factory.ts +154 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_16TestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_32TestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_64TestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_8TestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowanceScalarTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowanceTestsContract__factory.ts +1 -1
- package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract__factory.ts +190 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract__factory.ts +210 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract__factory.ts +260 -0
- package/typechain-types/factories/contracts/mocks/utils/mpc/index.ts +32 -8
- package/typechain-types/factories/contracts/mocks/wallet/PrivateERC20Wallet/PrivateERC20WalletMock__factory.ts +7 -7
- package/typechain-types/factories/contracts/token/PrivateERC20/IPrivateERC20__factory.ts +174 -42
- package/typechain-types/factories/contracts/token/PrivateERC20/PrivateERC20__factory.ts +174 -42
- package/typechain-types/factories/contracts/utils/mpc/MpcInterface.sol/ExtendedOperations__factory.ts +126 -0
- package/typechain-types/hardhat.d.ts +638 -80
- package/typechain-types/index.ts +78 -16
- package/contracts/mocks/utils/mpc/Arithmetic128BitTestsContract.sol +0 -260
- package/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract.sol +0 -264
- package/contracts/mocks/utils/mpc/Bitwise128BitTestsContract.sol +0 -91
- package/contracts/mocks/utils/mpc/Bitwise256BitTestsContract.sol +0 -91
- package/contracts/mocks/utils/mpc/Comparison128BitTestsContract.sol +0 -158
- package/contracts/mocks/utils/mpc/Comparison256BitTestsContract.sol +0 -158
- package/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract.sol +0 -185
- package/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract.sol +0 -190
- package/contracts/mocks/utils/mpc/README.md +0 -486
- package/test/utils/mpc/Unsigned128BitIntegers.test.ts +0 -945
- package/test/utils/mpc/Unsigned256BitIntegers.test.ts +0 -1124
- package/test/utils/mpc/helpers.ts +0 -77
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
|
5
|
+
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
6
|
+
import "@openzeppelin/contracts/utils/Strings.sol";
|
|
7
|
+
import "@openzeppelin/contracts/utils/Base64.sol";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @title SoulboundNodeNFT
|
|
11
|
+
* @notice ERC-721 Soulbound Node Identity NFT with ERC-5192 support
|
|
12
|
+
*
|
|
13
|
+
* @dev High-level properties:
|
|
14
|
+
* - Non-transferable (Soulbound)
|
|
15
|
+
* - ERC-5192 compliant (locked NFTs)
|
|
16
|
+
* - Only contract owner can mint
|
|
17
|
+
* - One NFT per wallet enforced
|
|
18
|
+
* - Minting can be paused by owner
|
|
19
|
+
* - Unlimited total supply
|
|
20
|
+
* - Burnable by token owner or contract owner
|
|
21
|
+
* - On-chain base64-encoded JSON metadata
|
|
22
|
+
* - Uses custom Solidity errors (gas efficient)
|
|
23
|
+
*
|
|
24
|
+
* @dev Intended use case:
|
|
25
|
+
* - Node / validator identity
|
|
26
|
+
* - Credentials
|
|
27
|
+
* - Non-transferable attestations
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/// -----------------------------------------------------------------------
|
|
31
|
+
/// ERC-5192: Minimal Soulbound NFTs
|
|
32
|
+
/// -----------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @dev ERC-5192 interface for signaling soulbound (non-transferable) NFTs.
|
|
36
|
+
* See: https://eips.ethereum.org/EIPS/eip-5192
|
|
37
|
+
*/
|
|
38
|
+
interface IERC5192 {
|
|
39
|
+
/// @notice Emitted when a token is locked (becomes soulbound)
|
|
40
|
+
event Locked(uint256 tokenId);
|
|
41
|
+
|
|
42
|
+
/// @notice Returns whether a token is locked (non-transferable)
|
|
43
|
+
function locked(uint256 tokenId) external view returns (bool);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// -----------------------------------------------------------------------
|
|
47
|
+
/// Custom errors (cheaper than revert strings)
|
|
48
|
+
/// -----------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
error InvalidRecipient();
|
|
51
|
+
error AlreadyOwnsNodeNFT();
|
|
52
|
+
error NodeNameRequired();
|
|
53
|
+
error NodeNameTooLong();
|
|
54
|
+
error SocialURLTooLong();
|
|
55
|
+
error NonexistentToken();
|
|
56
|
+
error SoulboundOperation();
|
|
57
|
+
error NotAuthorized();
|
|
58
|
+
error MintPaused();
|
|
59
|
+
|
|
60
|
+
/// -----------------------------------------------------------------------
|
|
61
|
+
/// Main contract
|
|
62
|
+
/// -----------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
contract SoulboundNodeNFT is ERC721, Ownable, IERC5192 {
|
|
65
|
+
using Strings for uint256;
|
|
66
|
+
|
|
67
|
+
/// @notice Last minted token ID (starts at 0, first mint = 1)
|
|
68
|
+
uint256 public nextTokenId;
|
|
69
|
+
|
|
70
|
+
/// @notice Maximum length (in bytes) for nodeName and socialURL
|
|
71
|
+
uint256 public constant MAX_TEXT_BYTES = 150;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @notice If true, minting is paused.
|
|
75
|
+
* @dev Burn operations are NOT affected.
|
|
76
|
+
*/
|
|
77
|
+
bool public mintPaused;
|
|
78
|
+
|
|
79
|
+
/// @notice Node-specific metadata stored on-chain
|
|
80
|
+
struct NodeData {
|
|
81
|
+
string nodeName; // required
|
|
82
|
+
string nodeImageURI; // optional (ipfs:// or https://)
|
|
83
|
+
string socialURL; // optional
|
|
84
|
+
bool isHot; // true = hot node, false = cold node
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// @dev tokenId => NodeData
|
|
88
|
+
mapping(uint256 => NodeData) private _nodeData;
|
|
89
|
+
|
|
90
|
+
/// @notice Emitted when a new node NFT is minted
|
|
91
|
+
event NodeMinted(
|
|
92
|
+
address indexed to,
|
|
93
|
+
uint256 indexed tokenId,
|
|
94
|
+
string nodeName,
|
|
95
|
+
string nodeImageURI,
|
|
96
|
+
string socialURL
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
/// @notice Emitted when mint pause state is changed
|
|
100
|
+
event MintPausedSet(bool paused);
|
|
101
|
+
|
|
102
|
+
/// @notice Emitted when a token's image URI is updated
|
|
103
|
+
event ImageURIUpdated(uint256 indexed tokenId, string newImageURI);
|
|
104
|
+
|
|
105
|
+
/// @notice Emitted when a token's social URL is updated
|
|
106
|
+
event SocialURLUpdated(uint256 indexed tokenId, string newSocialURL);
|
|
107
|
+
|
|
108
|
+
/// @notice Emitted when a token's node name is updated
|
|
109
|
+
event NodeNameUpdated(uint256 indexed tokenId, string newNodeName);
|
|
110
|
+
|
|
111
|
+
/// @notice Emitted when a token's hot/cold status is updated
|
|
112
|
+
event NodeHotStatusUpdated(uint256 indexed tokenId, bool isHot);
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @notice Contract constructor
|
|
116
|
+
* @param name_ ERC-721 collection name
|
|
117
|
+
* @param symbol_ ERC-721 collection symbol
|
|
118
|
+
* @param initialOwner Address that becomes contract owner
|
|
119
|
+
*/
|
|
120
|
+
constructor(
|
|
121
|
+
string memory name_,
|
|
122
|
+
string memory symbol_,
|
|
123
|
+
address initialOwner
|
|
124
|
+
) ERC721(name_, symbol_) {
|
|
125
|
+
_transferOwnership(initialOwner);
|
|
126
|
+
mintPaused = false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// -----------------------------------------------------------------------
|
|
130
|
+
/// Admin: pause / unpause minting
|
|
131
|
+
/// -----------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @notice Pause minting operations
|
|
135
|
+
* @dev Only the contract owner can call this function
|
|
136
|
+
* @dev Only affects minting, not burning operations
|
|
137
|
+
* @dev Emits MintPausedSet event with true
|
|
138
|
+
*/
|
|
139
|
+
function pauseMint() external onlyOwner {
|
|
140
|
+
mintPaused = true;
|
|
141
|
+
emit MintPausedSet(true);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @notice Unpause minting operations
|
|
146
|
+
* @dev Only the contract owner can call this function
|
|
147
|
+
* @dev Emits MintPausedSet event with false
|
|
148
|
+
*/
|
|
149
|
+
function unpauseMint() external onlyOwner {
|
|
150
|
+
mintPaused = false;
|
|
151
|
+
emit MintPausedSet(false);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// -----------------------------------------------------------------------
|
|
155
|
+
/// ERC-5192 (Soulbound signaling)
|
|
156
|
+
/// -----------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @notice Check whether a token is locked (soulbound)
|
|
160
|
+
* @param tokenId Token ID to check
|
|
161
|
+
* @return Always true for existing tokens
|
|
162
|
+
* @dev Reverts if token does not exist
|
|
163
|
+
*/
|
|
164
|
+
function locked(uint256 tokenId) external view override returns (bool) {
|
|
165
|
+
if (_ownerOf(tokenId) == address(0)) revert NonexistentToken();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @notice ERC-165 interface support
|
|
171
|
+
* @param interfaceId Interface identifier to check
|
|
172
|
+
* @return true if the contract supports the interface, false otherwise
|
|
173
|
+
* @dev Supports ERC721, ERC165, and ERC5192 interfaces
|
|
174
|
+
*/
|
|
175
|
+
function supportsInterface(
|
|
176
|
+
bytes4 interfaceId
|
|
177
|
+
) public view override(ERC721) returns (bool) {
|
|
178
|
+
return
|
|
179
|
+
interfaceId == type(IERC5192).interfaceId ||
|
|
180
|
+
super.supportsInterface(interfaceId);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// -----------------------------------------------------------------------
|
|
184
|
+
/// Minting (ONLY OWNER)
|
|
185
|
+
/// -----------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @notice Mint a new soulbound Node NFT
|
|
189
|
+
* @param to Recipient address
|
|
190
|
+
* @param nodeName Node name (required, <=150 bytes)
|
|
191
|
+
* @param nodeImageURI Optional image URI
|
|
192
|
+
* @param socialURL Optional social URL (<=150 bytes)
|
|
193
|
+
* @return tokenId The ID of the newly minted token
|
|
194
|
+
* @dev Restrictions:
|
|
195
|
+
* - Only contract owner can mint
|
|
196
|
+
* - Minting must not be paused
|
|
197
|
+
* - Recipient may own at most one NFT
|
|
198
|
+
* @dev Reverts if:
|
|
199
|
+
* - Minting is paused (MintPaused)
|
|
200
|
+
* - to is the zero address (InvalidRecipient)
|
|
201
|
+
* - to already owns an NFT (AlreadyOwnsNodeNFT)
|
|
202
|
+
* - nodeName is empty (NodeNameRequired)
|
|
203
|
+
* - nodeName exceeds MAX_TEXT_BYTES (NodeNameTooLong)
|
|
204
|
+
* - socialURL exceeds MAX_TEXT_BYTES (SocialURLTooLong)
|
|
205
|
+
* @dev Emits Locked and NodeMinted events
|
|
206
|
+
*/
|
|
207
|
+
function mintNode(
|
|
208
|
+
address to,
|
|
209
|
+
string calldata nodeName,
|
|
210
|
+
string calldata nodeImageURI,
|
|
211
|
+
string calldata socialURL
|
|
212
|
+
) external onlyOwner returns (uint256 tokenId) {
|
|
213
|
+
if (mintPaused) revert MintPaused();
|
|
214
|
+
if (to == address(0)) revert InvalidRecipient();
|
|
215
|
+
if (balanceOf(to) != 0) revert AlreadyOwnsNodeNFT();
|
|
216
|
+
if (bytes(nodeName).length == 0) revert NodeNameRequired();
|
|
217
|
+
if (bytes(nodeName).length > MAX_TEXT_BYTES) revert NodeNameTooLong();
|
|
218
|
+
if (bytes(socialURL).length > MAX_TEXT_BYTES) revert SocialURLTooLong();
|
|
219
|
+
|
|
220
|
+
tokenId = ++nextTokenId;
|
|
221
|
+
|
|
222
|
+
_safeMint(to, tokenId);
|
|
223
|
+
|
|
224
|
+
_nodeData[tokenId] = NodeData({
|
|
225
|
+
nodeName: nodeName,
|
|
226
|
+
nodeImageURI: nodeImageURI,
|
|
227
|
+
socialURL: socialURL,
|
|
228
|
+
// Default new nodes to hot; can be updated later via setNodeHotStatus
|
|
229
|
+
isHot: true
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
emit Locked(tokenId);
|
|
233
|
+
emit NodeMinted(to, tokenId, nodeName, nodeImageURI, socialURL);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @notice Get node metadata for a token
|
|
238
|
+
* @param tokenId Token ID to query
|
|
239
|
+
* @return NodeData struct containing nodeName, nodeImageURI, and socialURL
|
|
240
|
+
* @dev Reverts if the token does not exist (NonexistentToken)
|
|
241
|
+
*/
|
|
242
|
+
function nodeData(uint256 tokenId) external view returns (NodeData memory) {
|
|
243
|
+
if (_ownerOf(tokenId) == address(0)) revert NonexistentToken();
|
|
244
|
+
return _nodeData[tokenId];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @notice Returns whether a node is marked as hot or cold
|
|
249
|
+
* @param tokenId Token ID to query
|
|
250
|
+
* @return isHot True if the node is hot, false if cold
|
|
251
|
+
* @dev Reverts if the token does not exist (NonexistentToken)
|
|
252
|
+
*/
|
|
253
|
+
function isHotNode(uint256 tokenId) external view returns (bool isHot) {
|
|
254
|
+
if (_ownerOf(tokenId) == address(0)) revert NonexistentToken();
|
|
255
|
+
return _nodeData[tokenId].isHot;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* @notice Get token ID for a given owner address
|
|
260
|
+
* @param owner The address to query
|
|
261
|
+
* @return tokenId The ID of the token owned by the address, or 0 if none
|
|
262
|
+
* @dev Iterates over token IDs. Since mints are sequential and users only own 1, this is a linear scan.
|
|
263
|
+
* In a production environment with millions of NFTs an off-chain indexer is preferred,
|
|
264
|
+
* but for this scale and testnet use-case, this convenience function works.
|
|
265
|
+
*/
|
|
266
|
+
function tokenOfOwner(address owner) external view returns (uint256) {
|
|
267
|
+
if (owner == address(0)) revert InvalidRecipient();
|
|
268
|
+
if (balanceOf(owner) == 0) return 0;
|
|
269
|
+
|
|
270
|
+
// Since the user only has 1 NFT due to the mint restriction, we can find it
|
|
271
|
+
for (uint256 i = 1; i <= nextTokenId; ++i) {
|
|
272
|
+
if (_ownerOf(i) == owner) {
|
|
273
|
+
return i;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return 0; // Should never reach here if balanceOf > 0, but fallback
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @notice Update the image URI for a token
|
|
281
|
+
* @param tokenId Token ID to update
|
|
282
|
+
* @param newImageURI New image URI (ipfs:// or https://)
|
|
283
|
+
* @dev Token owner or contract owner can call this function
|
|
284
|
+
* @dev Reverts if:
|
|
285
|
+
* - Token does not exist (NonexistentToken)
|
|
286
|
+
* - Caller is not authorized (NotAuthorized)
|
|
287
|
+
* @dev Emits ImageURIUpdated event
|
|
288
|
+
*/
|
|
289
|
+
function setImageURI(
|
|
290
|
+
uint256 tokenId,
|
|
291
|
+
string calldata newImageURI
|
|
292
|
+
) external {
|
|
293
|
+
address tokenOwner = _ownerOf(tokenId);
|
|
294
|
+
if (tokenOwner == address(0)) revert NonexistentToken();
|
|
295
|
+
if (msg.sender != tokenOwner && msg.sender != owner())
|
|
296
|
+
revert NotAuthorized();
|
|
297
|
+
|
|
298
|
+
_nodeData[tokenId].nodeImageURI = newImageURI;
|
|
299
|
+
emit ImageURIUpdated(tokenId, newImageURI);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @notice Update the social URL for a token
|
|
304
|
+
* @param tokenId Token ID to update
|
|
305
|
+
* @param newSocialURL New social URL (<=150 bytes)
|
|
306
|
+
* @dev Token owner or contract owner can call this function
|
|
307
|
+
* @dev Reverts if:
|
|
308
|
+
* - Token does not exist (NonexistentToken)
|
|
309
|
+
* - newSocialURL exceeds MAX_TEXT_BYTES (SocialURLTooLong)
|
|
310
|
+
* - Caller is not authorized (NotAuthorized)
|
|
311
|
+
* @dev Emits SocialURLUpdated event
|
|
312
|
+
*/
|
|
313
|
+
function setSocialURL(
|
|
314
|
+
uint256 tokenId,
|
|
315
|
+
string calldata newSocialURL
|
|
316
|
+
) external {
|
|
317
|
+
address tokenOwner = _ownerOf(tokenId);
|
|
318
|
+
if (tokenOwner == address(0)) revert NonexistentToken();
|
|
319
|
+
if (msg.sender != tokenOwner && msg.sender != owner())
|
|
320
|
+
revert NotAuthorized();
|
|
321
|
+
if (bytes(newSocialURL).length > MAX_TEXT_BYTES)
|
|
322
|
+
revert SocialURLTooLong();
|
|
323
|
+
|
|
324
|
+
_nodeData[tokenId].socialURL = newSocialURL;
|
|
325
|
+
emit SocialURLUpdated(tokenId, newSocialURL);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* @notice Update the node name for a token
|
|
330
|
+
* @param tokenId Token ID to update
|
|
331
|
+
* @param newNodeName New node name (required, <=150 bytes)
|
|
332
|
+
* @dev Token owner or contract owner can call this function
|
|
333
|
+
* @dev Reverts if:
|
|
334
|
+
* - Token does not exist (NonexistentToken)
|
|
335
|
+
* - newNodeName is empty (NodeNameRequired)
|
|
336
|
+
* - newNodeName exceeds MAX_TEXT_BYTES (NodeNameTooLong)
|
|
337
|
+
* - Caller is not authorized (NotAuthorized)
|
|
338
|
+
* @dev Emits NodeNameUpdated event
|
|
339
|
+
*/
|
|
340
|
+
function setNodeName(
|
|
341
|
+
uint256 tokenId,
|
|
342
|
+
string calldata newNodeName
|
|
343
|
+
) external {
|
|
344
|
+
address tokenOwner = _ownerOf(tokenId);
|
|
345
|
+
if (tokenOwner == address(0)) revert NonexistentToken();
|
|
346
|
+
if (msg.sender != tokenOwner && msg.sender != owner())
|
|
347
|
+
revert NotAuthorized();
|
|
348
|
+
if (bytes(newNodeName).length == 0) revert NodeNameRequired();
|
|
349
|
+
if (bytes(newNodeName).length > MAX_TEXT_BYTES)
|
|
350
|
+
revert NodeNameTooLong();
|
|
351
|
+
|
|
352
|
+
_nodeData[tokenId].nodeName = newNodeName;
|
|
353
|
+
emit NodeNameUpdated(tokenId, newNodeName);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @notice Update the hot/cold status for a node
|
|
358
|
+
* @param tokenId Token ID to update
|
|
359
|
+
* @param isHot True to mark the node as hot, false to mark as cold
|
|
360
|
+
* @dev Contract owner can call this function
|
|
361
|
+
* @dev Reverts if:
|
|
362
|
+
* - Token does not exist (NonexistentToken)
|
|
363
|
+
* - Caller is not authorized (NotAuthorized)
|
|
364
|
+
* @dev Emits NodeHotStatusUpdated event
|
|
365
|
+
*/
|
|
366
|
+
function setNodeHotStatus(uint256 tokenId, bool isHot) external {
|
|
367
|
+
address tokenOwner = _ownerOf(tokenId);
|
|
368
|
+
if (tokenOwner == address(0)) revert NonexistentToken();
|
|
369
|
+
if (msg.sender != owner()) revert NotAuthorized();
|
|
370
|
+
|
|
371
|
+
_nodeData[tokenId].isHot = isHot;
|
|
372
|
+
emit NodeHotStatusUpdated(tokenId, isHot);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/// -----------------------------------------------------------------------
|
|
376
|
+
/// Soulbound restrictions (disable transfers & approvals)
|
|
377
|
+
/// -----------------------------------------------------------------------
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* @notice Approve an address to transfer a token (disabled for soulbound NFTs)
|
|
381
|
+
* @dev Always reverts with SoulboundOperation error since tokens cannot be transferred
|
|
382
|
+
* @dev Parameters match ERC721 interface but are unused
|
|
383
|
+
*/
|
|
384
|
+
function approve(
|
|
385
|
+
address /* operator */,
|
|
386
|
+
uint256 /* tokenId */
|
|
387
|
+
) public pure override {
|
|
388
|
+
revert SoulboundOperation();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* @notice Approve an operator for all tokens (disabled for soulbound NFTs)
|
|
393
|
+
* @dev Always reverts with SoulboundOperation error since tokens cannot be transferred
|
|
394
|
+
* @dev Parameters match ERC721 interface but are unused
|
|
395
|
+
*/
|
|
396
|
+
function setApprovalForAll(
|
|
397
|
+
address /* operator */,
|
|
398
|
+
bool /* approved */
|
|
399
|
+
) public pure override {
|
|
400
|
+
revert SoulboundOperation();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @notice Transfer a token (disabled for soulbound NFTs)
|
|
405
|
+
* @dev Always reverts with SoulboundOperation error since tokens are soulbound
|
|
406
|
+
* @dev Parameters match ERC721 interface but are unused
|
|
407
|
+
*/
|
|
408
|
+
function transferFrom(
|
|
409
|
+
address /* from */,
|
|
410
|
+
address /* to */,
|
|
411
|
+
uint256 /* tokenId */
|
|
412
|
+
) public pure override {
|
|
413
|
+
revert SoulboundOperation();
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/// -----------------------------------------------------------------------
|
|
417
|
+
/// Burning
|
|
418
|
+
/// -----------------------------------------------------------------------
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* @notice Burn a token, permanently removing it from circulation
|
|
422
|
+
* @param tokenId Token ID to burn
|
|
423
|
+
* @dev Callable by token owner or contract owner
|
|
424
|
+
* @dev Reverts if:
|
|
425
|
+
* - Caller is not the token owner and not the contract owner (NotAuthorized)
|
|
426
|
+
* - Token does not exist
|
|
427
|
+
* @dev Deletes the associated node data after burning
|
|
428
|
+
*/
|
|
429
|
+
function burn(uint256 tokenId) external {
|
|
430
|
+
address tokenOwner = ownerOf(tokenId);
|
|
431
|
+
if (msg.sender != tokenOwner && msg.sender != owner())
|
|
432
|
+
revert NotAuthorized();
|
|
433
|
+
|
|
434
|
+
_burn(tokenId);
|
|
435
|
+
delete _nodeData[tokenId];
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/// -----------------------------------------------------------------------
|
|
439
|
+
/// Metadata (on-chain JSON)
|
|
440
|
+
/// -----------------------------------------------------------------------
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* @notice Return token metadata as base64-encoded JSON
|
|
444
|
+
* @param tokenId Token ID to query
|
|
445
|
+
* @return A base64-encoded JSON data URI containing the token metadata
|
|
446
|
+
* @dev Returns on-chain JSON metadata as a data URI (data:application/json;base64,...)
|
|
447
|
+
* @dev Includes token name, description, attributes (node name, social URL), and optional image
|
|
448
|
+
* @dev Reverts if the token does not exist (NonexistentToken)
|
|
449
|
+
*/
|
|
450
|
+
function tokenURI(
|
|
451
|
+
uint256 tokenId
|
|
452
|
+
) public view override returns (string memory) {
|
|
453
|
+
if (_ownerOf(tokenId) == address(0)) revert NonexistentToken();
|
|
454
|
+
|
|
455
|
+
NodeData memory d = _nodeData[tokenId];
|
|
456
|
+
|
|
457
|
+
string memory attributes = string.concat(
|
|
458
|
+
'[{"trait_type":"Node name","value":"',
|
|
459
|
+
_escapeJSON(d.nodeName),
|
|
460
|
+
'"}',
|
|
461
|
+
',{"trait_type":"Social URL","value":"',
|
|
462
|
+
_escapeJSON(d.socialURL),
|
|
463
|
+
'"}]'
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
string memory imagePart = bytes(d.nodeImageURI).length == 0
|
|
467
|
+
? ""
|
|
468
|
+
: string.concat(',"image":"', _escapeJSON(d.nodeImageURI), '"');
|
|
469
|
+
|
|
470
|
+
string memory json = string.concat(
|
|
471
|
+
'{"name":"Node #',
|
|
472
|
+
tokenId.toString(),
|
|
473
|
+
'","description":"Soulbound Node NFT",',
|
|
474
|
+
'"attributes":',
|
|
475
|
+
attributes,
|
|
476
|
+
imagePart,
|
|
477
|
+
"}"
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
return
|
|
481
|
+
string.concat(
|
|
482
|
+
"data:application/json;base64,",
|
|
483
|
+
Base64.encode(bytes(json))
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/// -----------------------------------------------------------------------
|
|
488
|
+
/// Internal helpers
|
|
489
|
+
/// -----------------------------------------------------------------------
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @notice Escape special JSON characters in a string
|
|
493
|
+
* @param s The string to escape
|
|
494
|
+
* @return The escaped string with backslashes added before quotes and backslashes
|
|
495
|
+
* @dev Used internally to safely embed strings in JSON metadata
|
|
496
|
+
* @dev Escapes double quotes (") and backslashes (\) by prefixing them with a backslash
|
|
497
|
+
*/
|
|
498
|
+
function _escapeJSON(
|
|
499
|
+
string memory s
|
|
500
|
+
) internal pure returns (string memory) {
|
|
501
|
+
bytes memory b = bytes(s);
|
|
502
|
+
bytes memory out = new bytes(b.length * 2);
|
|
503
|
+
uint256 j;
|
|
504
|
+
|
|
505
|
+
for (uint256 i = 0; i < b.length; i++) {
|
|
506
|
+
bytes1 c = b[i];
|
|
507
|
+
if (c == bytes1('"') || c == bytes1("\\")) {
|
|
508
|
+
out[j++] = bytes1("\\");
|
|
509
|
+
out[j++] = c;
|
|
510
|
+
} else {
|
|
511
|
+
out[j++] = c;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
bytes memory trimmed = new bytes(j);
|
|
516
|
+
for (uint256 k = 0; k < j; k++) trimmed[k] = out[k];
|
|
517
|
+
return string(trimmed);
|
|
518
|
+
}
|
|
519
|
+
}
|