Skip to content

Commit

Permalink
feat!: Add batchCalls(...) to LSP7 & LSP8 (#810)
Browse files Browse the repository at this point in the history
* feat!: add `batchCalls(...)` to LSP7 & LSP8

* refactor: update interface ids in the constants

* test: add new tests

* docs: update auto-generated docs
  • Loading branch information
b00ste committed Nov 28, 2023
1 parent 07c56c1 commit be46796
Show file tree
Hide file tree
Showing 27 changed files with 1,637 additions and 19 deletions.
4 changes: 2 additions & 2 deletions constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export const INTERFACE_IDS = {
LSP1UniversalReceiver: '0x6bb56a14',
LSP1UniversalReceiverDelegate: '0xa245bbda',
LSP6KeyManager: '0x23f34c62',
LSP7DigitalAsset: '0xdaa746b7',
LSP8IdentifiableDigitalAsset: '0x30dc5278',
LSP7DigitalAsset: '0xb3c4928f',
LSP8IdentifiableDigitalAsset: '0x59bf8640',
LSP9Vault: '0x28af17e6',
LSP11BasicSocialRecovery: '0x049a28f1',
LSP14Ownable2Step: '0x94be5999',
Expand Down
11 changes: 11 additions & 0 deletions contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,15 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
bool[] memory force,
bytes[] memory data
) external;

/**
* @notice Executing the following batch of abi-encoded function calls on the contract: `data`.
*
* @dev Allows a caller to batch different function calls in one call. Perform a `delegatecall` on self, to call different functions with preserving the context.
* @param data An array of ABI encoded function calls to be called on the contract.
* @return results An array of abi-encoded data returned by the functions executed.
*/
function batchCalls(
bytes[] calldata data
) external returns (bytes[] memory results);
}
2 changes: 1 addition & 1 deletion contracts/LSP7DigitalAsset/LSP7Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.4;

// --- ERC165 interface ids
bytes4 constant _INTERFACEID_LSP7 = 0xdaa746b7;
bytes4 constant _INTERFACEID_LSP7 = 0xb3c4928f;

// --- Token Hooks

Expand Down
61 changes: 57 additions & 4 deletions contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import {
LSP7CannotSendWithAddressZero,
LSP7NotifyTokenReceiverContractMissingLSP1Interface,
LSP7NotifyTokenReceiverIsEOA,
OperatorAllowanceCannotBeIncreasedFromZero
OperatorAllowanceCannotBeIncreasedFromZero,
LSP7BatchCallFailed
} from "./LSP7Errors.sol";

// constants
Expand Down Expand Up @@ -97,6 +98,45 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
return _tokenOwnerBalances[tokenOwner];
}

// --- General functionality

/**
* @inheritdoc ILSP7DigitalAsset
*
* @custom:info It's not possible to send value along the functions call due to the use of `delegatecall`.
*/
function batchCalls(
bytes[] calldata data
) public virtual override returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i; i < data.length; ) {
(bool success, bytes memory result) = address(this).delegatecall(
data[i]
);

if (!success) {
// Look for revert reason and bubble it up if present
if (result.length != 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(result)
revert(add(32, result), returndata_size)
}
} else {
revert LSP7BatchCallFailed({callIndex: i});
}
}

results[i] = result;

unchecked {
++i;
}
}
}

// --- Operator functionality

/**
Expand Down Expand Up @@ -392,7 +432,14 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {

_tokenOwnerBalances[to] += amount;

emit Transfer(msg.sender, address(0), to, amount, force, data);
emit Transfer({
operator: msg.sender,
from: address(0),
to: to,
amount: amount,
force: force,
data: data
});

_afterTokenTransfer(address(0), to, amount, data);

Expand Down Expand Up @@ -452,7 +499,6 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {

_tokenOwnerBalances[from] -= amount;

emit Transfer(msg.sender, from, address(0), amount, false, data);
emit Transfer({
operator: msg.sender,
from: from,
Expand Down Expand Up @@ -563,7 +609,14 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
_tokenOwnerBalances[from] -= amount;
_tokenOwnerBalances[to] += amount;

emit Transfer(msg.sender, from, to, amount, force, data);
emit Transfer({
operator: msg.sender,
from: from,
to: to,
amount: amount,
force: force,
data: data
});

_afterTokenTransfer(from, to, amount, data);

Expand Down
6 changes: 6 additions & 0 deletions contracts/LSP7DigitalAsset/LSP7Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,9 @@ error LSP7TokenContractCannotHoldValue();
* @dev Reverts when token owner call {increaseAllowance} for an operator that does not have any allowance
*/
error OperatorAllowanceCannotBeIncreasedFromZero(address operator);

/**
* @dev Reverts when a batch call failed.
* @notice Batch call failed.
*/
error LSP7BatchCallFailed(uint256 callIndex);
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,15 @@ interface ILSP8IdentifiableDigitalAsset is IERC165, IERC725Y {
bool[] memory force,
bytes[] memory data
) external;

/**
* @notice Executing the following batch of abi-encoded function calls on the contract: `data`.
*
* @dev Allows a caller to batch different function calls in one call. Perform a `delegatecall` on self, to call different functions with preserving the context.
* @param data An array of ABI encoded function calls to be called on the contract.
* @return results An array of abi-encoded data returned by the functions executed.
*/
function batchCalls(
bytes[] calldata data
) external returns (bytes[] memory results);
}
2 changes: 1 addition & 1 deletion contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.4;

// --- ERC165 interface ids
bytes4 constant _INTERFACEID_LSP8 = 0x30dc5278;
bytes4 constant _INTERFACEID_LSP8 = 0x59bf8640;

// --- ERC725Y Data Keys

Expand Down
6 changes: 6 additions & 0 deletions contracts/LSP8IdentifiableDigitalAsset/LSP8Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,9 @@ error LSP8TokenContractCannotHoldValue();
* It can be set only once inside the constructor/initializer when the identifiable digital asset contract is being deployed.
*/
error LSP8TokenIdTypeNotEditable();

/**
* @dev Reverts when a batch call failed.
* @notice Batch call failed.
*/
error LSP8BatchCallFailed(uint256 callIndex);
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import {
LSP8TokenIdAlreadyMinted,
LSP8CannotSendToSelf,
LSP8NotifyTokenReceiverContractMissingLSP1Interface,
LSP8NotifyTokenReceiverIsEOA
LSP8NotifyTokenReceiverIsEOA,
LSP8BatchCallFailed
} from "./LSP8Errors.sol";

// constants
Expand Down Expand Up @@ -113,6 +114,45 @@ abstract contract LSP8IdentifiableDigitalAssetCore is
return _ownedTokens[tokenOwner].values();
}

// --- General functionality

/**
* @inheritdoc ILSP8IdentifiableDigitalAsset
*
* @custom:info It's not possible to send value along the functions call due to the use of `delegatecall`.
*/
function batchCalls(
bytes[] calldata data
) public virtual override returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i; i < data.length; ) {
(bool success, bytes memory result) = address(this).delegatecall(
data[i]
);

if (!success) {
// Look for revert reason and bubble it up if present
if (result.length != 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(result)
revert(add(32, result), returndata_size)
}
} else {
revert LSP8BatchCallFailed({callIndex: i});
}
}

results[i] = result;

unchecked {
++i;
}
}
}

// --- Operator functionality

/**
Expand Down
4 changes: 2 additions & 2 deletions docs/_interface_ids_table.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
| **LSP1UniversalReceiver** | `0x6bb56a14` | Interface of the LSP1 - Universal Receiver standard, an entry function for a contract to receive arbitrary information. |
| **LSP1UniversalReceiverDelegate** | `0xa245bbda` | Interface of the LSP1 - Universal Receiver Delegate standard. |
| **LSP6KeyManager** | `0x23f34c62` | Interface of the LSP6 - Key Manager standard, a contract acting as a controller of an ERC725 Account using predfined permissions. |
| **LSP7DigitalAsset** | `0xdaa746b7` | Interface of the LSP7 - Digital Asset standard, a fungible digital asset. |
| **LSP8IdentifiableDigitalAsset** | `0x30dc5278` | Interface of the LSP8 - Identifiable Digital Asset standard, a non-fungible digital asset. |
| **LSP7DigitalAsset** | `0xb3c4928f` | Interface of the LSP7 - Digital Asset standard, a fungible digital asset. |
| **LSP8IdentifiableDigitalAsset** | `0x59bf8640` | Interface of the LSP8 - Identifiable Digital Asset standard, a non-fungible digital asset. |
| **LSP9Vault** | `0x28af17e6` | Interface of LSP9 - Vault standard, a blockchain vault that can hold assets and interact with other smart contracts. |
| **LSP11BasicSocialRecovery** | `0x049a28f1` | Interface of the LSP11 - Basic Social Recovery standard, a contract to recover access control into an account. |
| **LSP14Ownable2Step** | `0x94be5999` | Interface of the LSP14 - Ownable 2-step standard, an extension of the [EIP173] (Ownable) standard with 2-step process to transfer or renounce ownership. |
Expand Down
66 changes: 66 additions & 0 deletions docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,45 @@ Get the number of tokens owned by `tokenOwner`. If the token is divisible (the [

<br/>

### batchCalls

:::note References

- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#batchcalls)
- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol)
- Function signature: `batchCalls(bytes[])`
- Function selector: `0x6963d438`

:::

:::info

It's not possible to send value along the functions call due to the use of `delegatecall`.

:::

```solidity
function batchCalls(bytes[] data) external nonpayable returns (bytes[] results);
```

_Executing the following batch of abi-encoded function calls on the contract: `data`._

Allows a caller to batch different function calls in one call. Perform a `delegatecall` on self, to call different functions with preserving the context.

#### Parameters

| Name | Type | Description |
| ------ | :-------: | -------------------------------------------------------------------- |
| `data` | `bytes[]` | An array of ABI encoded function calls to be called on the contract. |

#### Returns

| Name | Type | Description |
| --------- | :-------: | ---------------------------------------------------------------- |
| `results` | `bytes[]` | An array of abi-encoded data returned by the functions executed. |

<br/>

### decimals

:::note References
Expand Down Expand Up @@ -1475,6 +1514,33 @@ reverts when sending an `amount` of tokens larger than the current `balance` of

<br/>

### LSP7BatchCallFailed

:::note References

- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp7batchcallfailed)
- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol)
- Error signature: `LSP7BatchCallFailed(uint256)`
- Error hash: `0xb774c284`

:::

```solidity
error LSP7BatchCallFailed(uint256 callIndex);
```

_Batch call failed._

Reverts when a batch call failed.

#### Parameters

| Name | Type | Description |
| ----------- | :-------: | ----------- |
| `callIndex` | `uint256` | - |

<br/>

### LSP7CannotSendToSelf

:::note References
Expand Down
Loading

0 comments on commit be46796

Please sign in to comment.