SafeERC20.trySafeTransfer{,from} (#5483)
This commit is contained in:
5
.changeset/brown-seals-sing.md
Normal file
5
.changeset/brown-seals-sing.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'openzeppelin-solidity': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
`SafeERC20`: Add `trySafeTransfer` and `trySafeTransferFrom` that do not revert and return false if the transfer is not successful.
|
||||||
@ -42,6 +42,20 @@ library SafeERC20 {
|
|||||||
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
|
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
|
||||||
|
*/
|
||||||
|
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
|
||||||
|
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
|
||||||
|
*/
|
||||||
|
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
|
||||||
|
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
|
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
|
||||||
* non-reverting calls are assumed to be successful.
|
* non-reverting calls are assumed to be successful.
|
||||||
|
|||||||
@ -60,12 +60,24 @@ describe('SafeERC20', function () {
|
|||||||
.withArgs(this.token);
|
.withArgs(this.token);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns false on trySafeTransfer', async function () {
|
||||||
|
await expect(this.mock.$trySafeTransfer(this.token, this.receiver, 0n))
|
||||||
|
.to.emit(this.mock, 'return$trySafeTransfer')
|
||||||
|
.withArgs(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('reverts on transferFrom', async function () {
|
it('reverts on transferFrom', async function () {
|
||||||
await expect(this.mock.$safeTransferFrom(this.token, this.mock, this.receiver, 0n))
|
await expect(this.mock.$safeTransferFrom(this.token, this.mock, this.receiver, 0n))
|
||||||
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
|
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
|
||||||
.withArgs(this.token);
|
.withArgs(this.token);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns false on trySafeTransferFrom', async function () {
|
||||||
|
await expect(this.mock.$trySafeTransferFrom(this.token, this.mock, this.receiver, 0n))
|
||||||
|
.to.emit(this.mock, 'return$trySafeTransferFrom')
|
||||||
|
.withArgs(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('reverts on increaseAllowance', async function () {
|
it('reverts on increaseAllowance', async function () {
|
||||||
// Call to 'token.allowance' does not return any data, resulting in a decoding error (revert without reason)
|
// Call to 'token.allowance' does not return any data, resulting in a decoding error (revert without reason)
|
||||||
await expect(this.mock.$safeIncreaseAllowance(this.token, this.spender, 0n)).to.be.revertedWithoutReason();
|
await expect(this.mock.$safeIncreaseAllowance(this.token, this.spender, 0n)).to.be.revertedWithoutReason();
|
||||||
@ -94,12 +106,24 @@ describe('SafeERC20', function () {
|
|||||||
.withArgs(this.token);
|
.withArgs(this.token);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns false on trySafeTransfer', async function () {
|
||||||
|
await expect(this.mock.$trySafeTransfer(this.token, this.receiver, 0n))
|
||||||
|
.to.emit(this.mock, 'return$trySafeTransfer')
|
||||||
|
.withArgs(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('reverts on transferFrom', async function () {
|
it('reverts on transferFrom', async function () {
|
||||||
await expect(this.mock.$safeTransferFrom(this.token, this.mock, this.receiver, 0n))
|
await expect(this.mock.$safeTransferFrom(this.token, this.mock, this.receiver, 0n))
|
||||||
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
|
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
|
||||||
.withArgs(this.token);
|
.withArgs(this.token);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns false on trySafeTransferFrom', async function () {
|
||||||
|
await expect(this.mock.$trySafeTransferFrom(this.token, this.mock, this.receiver, 0n))
|
||||||
|
.to.emit(this.mock, 'return$trySafeTransferFrom')
|
||||||
|
.withArgs(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('reverts on increaseAllowance', async function () {
|
it('reverts on increaseAllowance', async function () {
|
||||||
await expect(this.mock.$safeIncreaseAllowance(this.token, this.spender, 0n))
|
await expect(this.mock.$safeIncreaseAllowance(this.token, this.spender, 0n))
|
||||||
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
|
.to.be.revertedWithCustomError(this.mock, 'SafeERC20FailedOperation')
|
||||||
@ -357,11 +381,23 @@ function shouldOnlyRevertOnErrors() {
|
|||||||
.withArgs(this.mock, this.receiver, 10n);
|
.withArgs(this.mock, this.receiver, 10n);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns true on trySafeTransfer', async function () {
|
||||||
|
await expect(this.mock.$trySafeTransfer(this.token, this.receiver, 10n))
|
||||||
|
.to.emit(this.mock, 'return$trySafeTransfer')
|
||||||
|
.withArgs(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("doesn't revert on transferFrom", async function () {
|
it("doesn't revert on transferFrom", async function () {
|
||||||
await expect(this.mock.$safeTransferFrom(this.token, this.owner, this.receiver, 10n))
|
await expect(this.mock.$safeTransferFrom(this.token, this.owner, this.receiver, 10n))
|
||||||
.to.emit(this.token, 'Transfer')
|
.to.emit(this.token, 'Transfer')
|
||||||
.withArgs(this.owner, this.receiver, 10n);
|
.withArgs(this.owner, this.receiver, 10n);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns true on trySafeTransferFrom', async function () {
|
||||||
|
await expect(this.mock.$trySafeTransferFrom(this.token, this.owner, this.receiver, 10n))
|
||||||
|
.to.emit(this.mock, 'return$trySafeTransferFrom')
|
||||||
|
.withArgs(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('approvals', function () {
|
describe('approvals', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user