Skip to content
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
186 changes: 105 additions & 81 deletions .gas-snapshot

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/rain.interpreter.interface
4 changes: 2 additions & 2 deletions src/generated/Rainterpreter.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0xa6b8194695aec2caaf40b73cd4e492ee12c82b68f6cb09e771bcd016eb1c1830);
bytes32 constant BYTECODE_HASH = bytes32(0x3e43bbd0c5fff7815d2f91ece0d52af18b1865c85e77585b4a45ae85d5f76e5a);

/// @dev The function pointers known to the interpreter for dynamic dispatch.
/// By setting these as a constant they can be inlined into the interpreter
/// and loaded at eval time for very low gas (~100) due to the compiler
/// optimising it to a single `codecopy` to build the in memory bytes array.
bytes constant OPCODE_FUNCTION_POINTERS =
hex"075b078d07b1093d0a060a180a2a0a430a670a9b0aac0abd0b5f0b7e0c3c0cec0d700eb20fe510de118011f81209121a121a122b12b812d112e512fe1317132a138c13da1428145914a714f51543162a";
hex"076c079e07c2094e0a170a290a3b0a540a780aac0abd0ace0b700b8f0c4d0cfd0d810ec30ff60c4d10ef11a1124312bb12cc12dd12dd12ee137b139413a813c113da13ed144f149d14eb151c156a15b8160616ed";
6 changes: 3 additions & 3 deletions src/generated/RainterpreterExpressionDeployer.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x8129e7266bfb3b783324f17acce944c8eceb52114fdc1e752e1e619af4b996a7);
bytes32 constant BYTECODE_HASH = bytes32(0x9a395815b697bd41925cbb3d0d4b7b4767fcb94df4859ced57b949802e98834f);

/// @dev The hash of the meta that describes the contract.
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0xa584ea21441427aadd44a19078a8f80abb0563d43e8da2bb0aab9f02a1205398);
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0xae18cb9cc13a8d1432bc47b0b1ec18bf14e54373e0b974a0f0234ba0477e3f05);

/// @dev The function pointers for the integrity check fns.
bytes constant INTEGRITY_FUNCTION_POINTERS =
hex"0df80e760eda1054105e105e10681071108c11321132118e12061213105e10681213105e1068105e10681054105410541054121d105e105e105e105e10541227124f122710541227124f124f1068121d";
hex"0e080e860eea1064106e106e10781081109c11421142119e12161223106e10781223106e1078106e106e106e10781064106410641064122d106e106e106e106e10641237125f123710641237125f125f1078122d";
8 changes: 4 additions & 4 deletions src/generated/RainterpreterParser.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x6f543e4dfa04d86b7bb9adbd4f2daf59cafd956a3d1f4d295cb680b8c286ac04);
bytes32 constant BYTECODE_HASH = bytes32(0x0d1bd1e36b938abb7312784026140ed159a9a59520f9d315c8836bcf14931c55);

/// @dev The parse meta that is used to lookup word definitions.
/// The structure of the parse meta is:
Expand All @@ -29,7 +29,7 @@ bytes32 constant BYTECODE_HASH = bytes32(0x6f543e4dfa04d86b7bb9adbd4f2daf59cafd9
/// bit count of the previous bloom filter. If we reach the end of the bloom
/// filters then we have a miss.
bytes constant PARSE_META =
hex"010e000088000004102c00000c8880800d24a0148109008880010000140554a010000d1dbb6302525f2211999fd50f6c01da1871b5110a2139801a69a80f00a4ed3f27ba92e12348361e2628c11e089ebd5e07e235981bb5fc9e24d9ace210e92356163994150b7dfb1509bb6451066c83bf215797780c4960e303fa47a905e82f0c1d4efea02224d7751fbfd64115d6a860135832fd1cb8491f20be2c7c01d81c842562708d0efdd5d8142466661747b9490477ee181251471f19e0ddd31e3436fb";
hex"0119204182060c080006c6c1800000012200200282040029000100680901208400200d1e98a71dd6ea18118eba9713065d302481e5d904ac72c00e7ff5a229ae0c8706fb9c2528c7bc2312fec72818d295ed23d9a6c50063d58d012ba0980c6cbb42216d6b2403ae6396082c4e962041d78a16172ab51a5b8f1d15f871180f48502f1fb9d38d0be7f2e71cf1ff4d19d140c2271a1e3510abcc6405b702d7020f038b07ee624a1e4143471482e75b1b60aa350a3d21ff174c80cb22cc4a6a26023c0409a046dc259b3887";

/// @dev The build depth of the parser meta.

Expand All @@ -39,11 +39,11 @@ uint8 constant PARSE_META_BUILD_DEPTH = 2;
/// These positional indexes all map to the same indexes looked up in the parse
/// meta.
bytes constant OPERAND_HANDLER_FUNCTION_POINTERS =
hex"19db19db19db1ab01bc71bc71bc71ab01ab019db19db19db1bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc71bc719db1bc71bc7";
hex"19eb19eb19eb1ac01bd71bd71bd71ac01ac019eb19eb19eb1bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd71bd719eb1bd71bd7";

/// @dev Every two bytes is a function pointer for a literal parser.
/// Literal dispatches are determined by the first byte(s) of the literal
/// rather than a full word lookup, and are done with simple conditional
/// jumps as the possibilities are limited compared to the number of words we
/// have.
bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"1523175517981836";
bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"1533176517a81846";
27 changes: 18 additions & 9 deletions src/lib/op/LibAllStandardOps.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {LibOpERC20BalanceOf} from "./erc20/LibOpERC20BalanceOf.sol";
import {LibOpERC20TotalSupply} from "./erc20/LibOpERC20TotalSupply.sol";

import {LibOpUint256ERC721BalanceOf} from "./erc721/uint256/LibOpUint256ERC721BalanceOf.sol";
import {LibOpERC721BalanceOf} from "./erc721/LibOpERC721BalanceOf.sol";
import {LibOpERC721OwnerOf} from "./erc721/LibOpERC721OwnerOf.sol";

import {LibOpERC5313OwnerNP} from "./erc5313/LibOpERC5313OwnerNP.sol";
Expand Down Expand Up @@ -105,7 +106,7 @@ import {LibParseLiteralHex} from "../parse/literal/LibParseLiteralHex.sol";
import {LibParseLiteralSubParseable} from "../parse/literal/LibParseLiteralSubParseable.sol";

/// @dev Number of ops currently provided by `AllStandardOps`.
uint256 constant ALL_STANDARD_OPS_LENGTH = 40;
uint256 constant ALL_STANDARD_OPS_LENGTH = 42;

/// @title LibAllStandardOps
/// @notice Every opcode available from the core repository laid out as a single
Expand Down Expand Up @@ -183,10 +184,14 @@ library LibAllStandardOps {
AuthoringMetaV2(
"erc20-total-supply", "Gets the total supply of an erc20 token. The input is the token address."
),
// AuthoringMetaV2(
// "uint256-erc721-balance-of",
// "Gets the balance of an erc721 token for an account as a uint256 value. The first input is the token address and the second is the account address."
// ),
AuthoringMetaV2(
"uint256-erc721-balance-of",
"Gets the balance of an erc721 token for an account as a uint256 value. The first input is the token address and the second is the account address. Returns a uint256 rather than a float."
),
AuthoringMetaV2(
"erc721-balance-of",
"Gets the balance of an erc721 token for an account. The first input is the token address and the second is the account address."
),
AuthoringMetaV2(
"erc721-owner-of",
"Gets the owner of an erc721 token. The first input is the token address and the second is the token id."
Expand Down Expand Up @@ -397,8 +402,10 @@ library LibAllStandardOps {
LibParseOperand.handleOperandDisallowed,
// erc20-total-supply
LibParseOperand.handleOperandDisallowed,
// // uint256-erc721-balance-of
// LibParseOperand.handleOperandDisallowed,
// uint256-erc721-balance-of
LibParseOperand.handleOperandDisallowed,
// erc721-balance-of
LibParseOperand.handleOperandDisallowed,
// erc721-owner-of
LibParseOperand.handleOperandDisallowed,
// erc5313-owner
Expand Down Expand Up @@ -560,7 +567,8 @@ library LibAllStandardOps {
LibOpERC20Allowance.integrity,
LibOpERC20BalanceOf.integrity,
LibOpERC20TotalSupply.integrity,
// LibOpUint256ERC721BalanceOf.integrity,
LibOpUint256ERC721BalanceOf.integrity,
LibOpERC721BalanceOf.integrity,
LibOpERC721OwnerOf.integrity,
LibOpERC5313OwnerNP.integrity,
LibOpBlockNumber.integrity,
Expand Down Expand Up @@ -672,7 +680,8 @@ library LibAllStandardOps {
LibOpERC20Allowance.run,
LibOpERC20BalanceOf.run,
LibOpERC20TotalSupply.run,
// LibOpUint256ERC721BalanceOf.run,
LibOpUint256ERC721BalanceOf.run,
LibOpERC721BalanceOf.run,
LibOpERC721OwnerOf.run,
LibOpERC5313OwnerNP.run,
LibOpBlockNumber.run,
Expand Down
54 changes: 54 additions & 0 deletions src/lib/op/erc721/LibOpERC721BalanceOf.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: CAL
pragma solidity ^0.8.18;

import {InterpreterState} from "../../state/LibInterpreterState.sol";
import {OperandV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {Pointer} from "rain.solmem/lib/LibPointer.sol";
import {IERC721} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import {LibDecimalFloat, Float} from "rain.math.float/lib/LibDecimalFloat.sol";
import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {IntegrityCheckState} from "../../integrity/LibIntegrityCheck.sol";

/// @title LibOpERC721BalanceOf
/// @notice Opcode for getting the current ERC721 balance of an account.
library LibOpERC721BalanceOf {
function integrity(IntegrityCheckState memory, OperandV2) internal pure returns (uint256, uint256) {
// Always 2 inputs, the token and the account.
// Always 1 output, the balance.
return (2, 1);
}

function run(InterpreterState memory, OperandV2, Pointer stackTop) internal view returns (Pointer) {
uint256 token;
uint256 account;
assembly ("memory-safe") {
token := mload(stackTop)
stackTop := add(stackTop, 0x20)
account := mload(stackTop)
}
uint256 tokenBalance = IERC721(address(uint160(token))).balanceOf(address(uint160(account)));

Float tokenBalanceFloat = LibDecimalFloat.fromFixedDecimalLosslessPacked(tokenBalance, 0);

assembly ("memory-safe") {
mstore(stackTop, tokenBalanceFloat)
}
return stackTop;
}

function referenceFn(InterpreterState memory, OperandV2, StackItem[] memory inputs)
internal
view
returns (StackItem[] memory)
{
address token = address(uint160(uint256(StackItem.unwrap(inputs[0]))));
address account = address(uint160(uint256(StackItem.unwrap(inputs[1]))));
uint256 tokenBalance = IERC721(token).balanceOf(account);

Float tokenBalanceFloat = LibDecimalFloat.fromFixedDecimalLosslessPacked(tokenBalance, 0);

StackItem[] memory outputs = new StackItem[](1);
outputs[0] = StackItem.wrap(Float.unwrap(tokenBalanceFloat));
return outputs;
}
}
15 changes: 8 additions & 7 deletions src/lib/op/erc721/uint256/LibOpUint256ERC721BalanceOf.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Pointer} from "rain.solmem/lib/LibPointer.sol";
import {IntegrityCheckState} from "../../../integrity/LibIntegrityCheck.sol";
import {OperandV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {InterpreterState} from "../../../state/LibInterpreterState.sol";
import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";

/// @title OpUint256ERC721BalanceOf
/// @notice Opcode for getting the current erc721 balance of an account.
Expand All @@ -31,16 +32,16 @@ library LibOpUint256ERC721BalanceOf {
return stackTop;
}

function referenceFn(InterpreterState memory, OperandV2, uint256[] memory inputs)
function referenceFn(InterpreterState memory, OperandV2, StackItem[] memory inputs)
internal
view
returns (uint256[] memory)
returns (StackItem[] memory)
{
uint256 token = inputs[0];
uint256 account = inputs[1];
uint256 tokenBalance = IERC721(address(uint160(token))).balanceOf(address(uint160(account)));
uint256[] memory outputs = new uint256[](1);
outputs[0] = tokenBalance;
address token = address(uint160(uint256(StackItem.unwrap(inputs[0]))));
address account = address(uint160(uint256(StackItem.unwrap(inputs[1]))));
uint256 tokenBalance = IERC721(token).balanceOf(account);
StackItem[] memory outputs = new StackItem[](1);
outputs[0] = StackItem.wrap(bytes32(tokenBalance));
return outputs;
}
}
148 changes: 148 additions & 0 deletions test/src/lib/op/erc721/LibOpUint256ERC721BalanceOf.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {OpTest} from "test/abstract/OpTest.sol";
import {IntegrityCheckState, BadOpInputsLength} from "src/lib/integrity/LibIntegrityCheck.sol";
import {LibOpERC721BalanceOf} from "src/lib/op/erc721/LibOpERC721BalanceOf.sol";
import {IERC721} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import {
IInterpreterV4,
FullyQualifiedNamespace,
OperandV2,
SourceIndexV2,
EvalV4
} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {LibContext} from "rain.interpreter.interface/lib/caller/LibContext.sol";
import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV3.sol";
import {UnexpectedOperand} from "src/error/ErrParse.sol";
import {LibOperand} from "test/lib/operand/LibOperand.sol";
import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol";
import {LibDecimalFloat, Float, LossyConversionToFloat} from "rain.math.float/lib/LibDecimalFloat.sol";

/// @title LibOpERC721BalanceOfTest
/// @notice Test the opcode for getting the balance of an erc721 token.
contract LibOpERC721BalanceOfTest is OpTest {
function testOpERC721BalanceOfIntegrity(
IntegrityCheckState memory state,
uint8 inputs,
uint8 outputs,
uint16 operandData
) external pure {
inputs = uint8(bound(inputs, 0, 0x0F));
outputs = uint8(bound(outputs, 0, 0x0F));
(uint256 calcInputs, uint256 calcOutputs) =
LibOpERC721BalanceOf.integrity(state, LibOperand.build(inputs, outputs, operandData));

assertEq(calcInputs, 2);
assertEq(calcOutputs, 1);
}

function testOpERC721BalanceOfRun(address token, address account, uint256 balance, uint16 operandData) external {
assumeEtchable(token);
vm.etch(token, hex"fe");

(, bool lossless) = LibDecimalFloat.fromFixedDecimalLossyPacked(balance, 0);
vm.assume(lossless);

StackItem[] memory inputs = new StackItem[](2);
inputs[0] = StackItem.wrap(bytes32(uint256(uint160(token))));
inputs[1] = StackItem.wrap(bytes32(uint256(uint160(account))));
OperandV2 operand = LibOperand.build(2, 1, operandData);

// invalid token
vm.mockCall(token, abi.encodeWithSelector(IERC721.balanceOf.selector, account), abi.encode(balance));
// called once for reference, once for run
vm.expectCall(token, abi.encodeWithSelector(IERC721.balanceOf.selector, account), 2);

opReferenceCheck(
opTestDefaultInterpreterState(),
operand,
LibOpERC721BalanceOf.referenceFn,
LibOpERC721BalanceOf.integrity,
LibOpERC721BalanceOf.run,
inputs
);
}

function testOpERC721BalanceOfEvalHappy(address token, address account, uint256 balance) public {
bytes memory bytecode = iDeployer.parse2(
bytes(
string.concat(
"_: erc721-balance-of(", Strings.toHexString(token), " ", Strings.toHexString(account), ");"
)
)
);

(, bool lossless) = LibDecimalFloat.fromFixedDecimalLossyPacked(balance, 0);
vm.assume(lossless);

assumeEtchable(token);
vm.etch(token, hex"fe");
vm.mockCall(token, abi.encodeWithSelector(IERC721.balanceOf.selector, account), abi.encode(balance));
vm.expectCall(token, abi.encodeWithSelector(IERC721.balanceOf.selector, account), 1);

(StackItem[] memory stack, bytes32[] memory kvs) = iInterpreter.eval4(
EvalV4({
store: iStore,
namespace: FullyQualifiedNamespace.wrap(0),
bytecode: bytecode,
sourceIndex: SourceIndexV2.wrap(0),
context: LibContext.build(new bytes32[][](0), new SignedContextV1[](0)),
inputs: new StackItem[](0),
stateOverlay: new bytes32[](0)
})
);
assertEq(stack.length, 1);
assertEq(StackItem.unwrap(stack[0]), bytes32(balance));
assertEq(kvs.length, 0);
}

/// Test that balance of without inputs fails integrity check.
function testOpERC721BalanceOfIntegrityFail0() external {
vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 0, 2, 0));
bytes memory bytecode = iDeployer.parse2("_: erc721-balance-of();");
(bytecode);
}

/// Test that balance of with one input fails integrity check.
function testOpERC721BalanceOfIntegrityFail1() external {
vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 1, 2, 1));
bytes memory bytecode = iDeployer.parse2("_: erc721-balance-of(0x00);");
(bytecode);
}

/// Test that balance of with three inputs fails integrity check.
function testOpERC721BalanceOfIntegrityFail3() external {
vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 3, 2, 3));
bytes memory bytecode = iDeployer.parse2("_: erc721-balance-of(0x00 0x01 0x02);");
(bytecode);
}

/// Test that operand fails integrity check.
function testOpERC721BalanceOfIntegrityFailOperand() external {
vm.expectRevert(abi.encodeWithSelector(UnexpectedOperand.selector));
bytes memory bytecode = iDeployer.parse2("_: erc721-balance-of<0>(0x00 0x01);");
(bytecode);
}

function testOpERC721BalanceOfZeroInputs() external {
checkBadInputs("_: erc721-balance-of();", 0, 2, 0);
}

function testOpERC721BalanceOfOneInput() external {
checkBadInputs("_: erc721-balance-of(0x00);", 1, 2, 1);
}

function testOpERC721BalanceOfThreeInputs() external {
checkBadInputs("_: erc721-balance-of(0x00 0x01 0x02);", 3, 2, 3);
}

function testOpERC721BalanceOfZeroOutputs() external {
checkBadOutputs(": erc721-balance-of(0x00 0x01);", 2, 1, 0);
}

function testOpERC721BalanceOfTwoOutputs() external {
checkBadOutputs("_ _: erc721-balance-of(0x00 0x01);", 2, 1, 2);
}
}
Loading
Loading