diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a0b6f0d7..2806c9f43 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,8 @@ Since the `@lukso/lsp-smart-contracts` is an Open Source project, we welcome con - report bug and issues. - introduce new features or bug fixes. +Any non-trivial code contribution **must be first discussed with the maintainers and the developer community in an [issue](https://github.com/lukso-network/lsp-smart-contracts/issues/new/choose)**. Only very minor changes are accepted without prior discussion. + ## **Clone project** Our project uses submodules, we recommend you to clone our repository using the following command: diff --git a/README.md b/README.md index aa4d42795..a8c138b5f 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,10 @@ The following audits and formal verification were conducted. All high-level issu - MiloTruck, 2023-11-31, Final Result: [MiloTruck_audit_2023_11_31.pdf](./audits/MiloTruck_audit_2023_11_31.pdf) - MiloTruck, 2024-01-24, Final Result: [MiloTruck_audit_2024_01_24.pdf](./audits/MiloTruck_audit_2024_01_24.pdf) +## Contribute + +The implementation contracts of the [LSPs](https://github.com/lukso-network/LIPs) exist thanks to their contributors. There are many ways you can participate and help build high quality software. Check out the [contribution guidelines](./CONTRIBUTING.md)! + ## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): diff --git a/packages/lsp-smart-contracts/tests/LSP7DigitalAsset/LSP7DigitalAsset.behaviour.ts b/packages/lsp-smart-contracts/tests/LSP7DigitalAsset/LSP7DigitalAsset.behaviour.ts index 89485e060..687f2092b 100644 --- a/packages/lsp-smart-contracts/tests/LSP7DigitalAsset/LSP7DigitalAsset.behaviour.ts +++ b/packages/lsp-smart-contracts/tests/LSP7DigitalAsset/LSP7DigitalAsset.behaviour.ts @@ -2207,6 +2207,80 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise { + describe('when the caller is the tokenOwner', () => { + it('should succeed', async () => { + const tokenOwner = context.accounts.owner.address; + const recipient = context.accounts.anyone.address; + const amount = 0; + + await expect( + context.lsp7 + .connect(context.accounts.owner) + .transfer(tokenOwner, recipient, amount, true, '0x'), + ) + .to.emit(context.lsp7, 'Transfer') + .withArgs(tokenOwner, tokenOwner, recipient, amount, true, '0x'); + }); + }); + + describe('when the caller is the operator', () => { + describe("when the caller doesn't have an authorized amount", () => { + it('should revert', async () => { + const operator = context.accounts.operator.address; + const tokenOwner = context.accounts.owner.address; + const recipient = context.accounts.anyone.address; + const amount = 0; + + await expect( + context.lsp7 + .connect(context.accounts.operator) + .transfer(tokenOwner, recipient, amount, true, '0x'), + ) + .to.be.revertedWithCustomError(context.lsp7, 'LSP7AmountExceedsAuthorizedAmount') + .withArgs(tokenOwner, 0, operator, amount); + }); + }); + describe('when the caller have an authorized amount', () => { + it('should succeed', async () => { + const operator = context.accounts.operator.address; + const tokenOwner = context.accounts.owner.address; + const recipient = context.accounts.anyone.address; + const amountAuthorized = 100; + const amount = 0; + + // pre-conditions + await context.lsp7 + .connect(context.accounts.owner) + .authorizeOperator(operator, amountAuthorized, '0x'); + expect(await context.lsp7.authorizedAmountFor(operator, tokenOwner)).to.equal( + amountAuthorized, + ); + + await expect( + context.lsp7 + .connect(context.accounts.operator) + .transfer(tokenOwner, recipient, amount, true, '0x'), + ) + .to.emit(context.lsp7, 'Transfer') + .withArgs(operator, tokenOwner, recipient, amount, true, '0x'); + }); + }); + }); + + describe('when making a call with sending value', () => { + it('should revert', async () => { + const amountSent = 200; + await expect( + context.accounts.anyone.sendTransaction({ + to: await context.lsp7.getAddress(), + value: amountSent, + }), + ).to.be.revertedWithCustomError(context.lsp7, 'LSP7TokenContractCannotHoldValue'); + }); + }); + }); + describe('batchCalls', () => { describe('when using one function', () => { describe('using `mint(...)`', () => { diff --git a/packages/lsp7-contracts/contracts/LSP7DigitalAsset.sol b/packages/lsp7-contracts/contracts/LSP7DigitalAsset.sol index 8a0e4121d..442c0df4d 100644 --- a/packages/lsp7-contracts/contracts/LSP7DigitalAsset.sol +++ b/packages/lsp7-contracts/contracts/LSP7DigitalAsset.sol @@ -719,7 +719,7 @@ abstract contract LSP7DigitalAsset is operator ]; - if (amountToSpend > authorizedAmount) { + if (authorizedAmount == 0 || amountToSpend > authorizedAmount) { revert LSP7AmountExceedsAuthorizedAmount( tokenOwner, authorizedAmount, diff --git a/packages/lsp7-contracts/contracts/LSP7DigitalAssetInitAbstract.sol b/packages/lsp7-contracts/contracts/LSP7DigitalAssetInitAbstract.sol index 1cd27ebee..ab256ade1 100644 --- a/packages/lsp7-contracts/contracts/LSP7DigitalAssetInitAbstract.sol +++ b/packages/lsp7-contracts/contracts/LSP7DigitalAssetInitAbstract.sol @@ -733,7 +733,7 @@ abstract contract LSP7DigitalAssetInitAbstract is operator ]; - if (amountToSpend > authorizedAmount) { + if (authorizedAmount == 0 || amountToSpend > authorizedAmount) { revert LSP7AmountExceedsAuthorizedAmount( tokenOwner, authorizedAmount,