Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Add batchCalls(...) to LSP7 & LSP8 #810

Merged
merged 4 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading