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

Metadata URI #13

Merged
merged 15 commits into from
May 24, 2024
100 changes: 70 additions & 30 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,74 @@
name: test
name: CI

on: workflow_dispatch
on: [push]

env:
FOUNDRY_PROFILE: ci
concurrency:
group: ${{ github.workflow}}-${{github.ref}}
cancel-in-progress: true

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run Forge build
run: |
forge --version
forge build --sizes
id: build

- name: Run Forge tests
run: |
forge test -vvv
id: test
lint:
name: Run Linters
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [18.x]

steps:
- uses: actions/checkout@v3

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "yarn"

- name: Install dependencies
run: yarn --frozen-lockfile --network-concurrency 1

- run: yarn lint:check

forge:
name: Run Unit and E2E Tests
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.x]

steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
cache: true

- name: Install forge dependencies
run: forge install

- name: Install yarn dependencies
run: yarn install --frozen-lockfile

- name: Build
run: forge build

- name: "Create env file"
run: |
touch .env
echo ARB_MAINNET_RPC="${{ secrets.ARB_MAINNET_RPC }}" >> .env
echo ARB_MAINNET_DEPLOYER_PK="${{ secrets.ARB_MAINNET_DEPLOYER_PK }}" >> .env
cat .env

- name: Run tests
shell: bash
run: yarn test
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"private": true,
"description": "Seaport Zone SIP-15 and EIP-7496",
"homepage": "https://opendollar.com/",
"license": "GNU AGPL",
"author": "OpenFi Foundation",
"repository": {
"type": "git",
"url": "git+https://github.com/open-dollar/od-seaport-zone.git"
},
"license": "GNU AGPL",
"author": "OpenFi Foundation",
"contributors": [
"pi0neerpat (https://github.com/pi0neerpat)",
"daopunk (https://github.com/daopunk)",
Expand All @@ -25,7 +25,7 @@
"test:coverage": "forge coverage --report lcov && lcov --ignore-errors unused --remove lcov.info 'node_modules/*' 'script/*' 'test/*' 'src/contracts/for-test/*' 'src/libraries/*' -o lcov.info.pruned && mv lcov.info.pruned lcov.info && genhtml -o coverage-report lcov.info"
},
"dependencies": {
"@opendollar/contracts": "^0.0.0-e31c2151",
"@opendollar/contracts": "^0.0.0-04ddd4b0",
"@openzeppelin/contracts": "^4.9.6"
},
"devDependencies": {
Expand All @@ -34,4 +34,4 @@
"solhint-plugin-defi-wonderland": "^1.1.3",
"sort-package-json": "^2.8.0"
}
}
}
53 changes: 39 additions & 14 deletions src/contracts/Vault721Adapter.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.20;

import {IERC7496} from 'shipyard-core/src/dynamic-traits/interfaces/IERC7496.sol';
import {Base64} from '@openzeppelin/utils/Base64.sol';
import {IVault721} from '@opendollar/interfaces/proxies/IVault721.sol';
import {IERC7496} from 'shipyard-core/src/dynamic-traits/interfaces/IERC7496.sol';

/**
* @title Adds support for ERC7496 to an existing ERC721
* @author OpenFi Foundation
* @notice IERC7496 events are never emitted since NFVState is tracked in Vault721
*/
contract Vault721Adapter is IERC7496 {
bytes32 public constant COLLATERAL = keccak256('COLLATERAL');
bytes32 public constant DEBT = keccak256('DEBT');
string public constant JSON_OPEN = '{"traits":{"';
string public constant JSON_DISPLAYNAME = '":{"displayName":"';
string public constant JSON_DATATYPE = '","dataType":{"type": "string","minLength":1},"validateOnSale": "';
string public constant JSON_CLOSE = '}}';

IVault721 public vault721;

Expand Down Expand Up @@ -41,27 +48,45 @@ contract Vault721Adapter is IERC7496 {
}

/**
* @dev ???
* @dev get NFVState from Vault721
* @notice return values are not hashed to enable enforceable condition in zone
*/
function getTraitMetadataURI() external view returns (string memory _uri) {
_uri = '?';
function _getTraitValue(uint256 _tokenId, bytes32 _traitKey) internal view returns (bytes32) {
IVault721.NFVState memory _nfvState = vault721.getNfvState(_tokenId);
if (_traitKey == COLLATERAL) return bytes32(_nfvState.collateral);
if (_traitKey == DEBT) return bytes32(_nfvState.debt);
else revert UnknownTraitKeys();
}

/**
* @dev setTrait is disabled; NFVState is found in Vault721
* @dev return onchain data uri of trait details
*/
function setTrait(uint256, bytes32, bytes32) external {
revert Disabled();
function getTraitMetadataURI() external view returns (string memory _uri) {
string memory _json = _formatJsonMetaData();
_uri = string.concat('data:application/json;base64,', Base64.encode(bytes(_json)));
}

function _formatJsonMetaData() internal pure returns (string memory _json) {
_json = string.concat(
JSON_OPEN,
'collateral',
JSON_DISPLAYNAME,
'Collateral',
JSON_DATATYPE,
'requireUintGte"}',
',"debt',
JSON_DISPLAYNAME,
'Debt',
JSON_DATATYPE,
'requireUintLte"}',
JSON_CLOSE
);
}

/**
* @dev get NFVState from Vault721
* @notice return values are not hashed to enable enforceable condition in zone
* @dev setTrait is disabled; NFVState is found in Vault721
*/
function _getTraitValue(uint256 _tokenId, bytes32 _traitKey) internal view returns (bytes32) {
IVault721.NFVState memory _nfvState = vault721.getNfvState(_tokenId);
if (_traitKey == COLLATERAL) return bytes32(_nfvState.collateral);
if (_traitKey == DEBT) return bytes32(_nfvState.debt);
else revert UnknownTraitKeys();
function setTrait(uint256, bytes32, bytes32) external {
revert Disabled();
}
}
23 changes: 20 additions & 3 deletions test/unit/Vault721Adapter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ contract Base is Test {

contract Unit_Vault721Adapter_SetUp is Base {
function test_initialize() public {
assertTrue(adapter.vault721() == vault721);
}

function test_constants() public {
assertTrue(adapter.COLLATERAL() == C);
assertTrue(adapter.DEBT() == D);
assertTrue(adapter.vault721() == vault721);
}

function test_mockCall(uint256 _tokenId, nfvValue memory _nfvValue) public nfvValues(_nfvValue) {
Expand Down Expand Up @@ -129,9 +132,23 @@ contract Unit_Vault721Adapter is Base {
);
}

/**
* @notice encoded json data that OpenSea uses to enforce rules about traits:
*
* {"traits":{"collateral":{"displayName":"Collateral","dataType":{"type": "string","minLength":1},"validateOnSale": "requireUintGte"},"debt":{"displayName":"Debt","dataType":{"type": "string","minLength":1},"validateOnSale": "requireUintLte"}}}
* to base64
*/
function test_getTraitMetadataURI() public {
bytes32 _uri = bytes32(abi.encode(adapter.getTraitMetadataURI()));
assertEq(_uri, bytes32(abi.encode('?')));
string memory _uri = adapter.getTraitMetadataURI();
emit log_named_string('Metadata URI', _uri);
assertEq(
bytes32(bytes(_uri)),
bytes32(
bytes(
'data:application/json;base64,eyJ0cmFpdHMiOnsiY29sbGF0ZXJhbCI6eyJkaXNwbGF5TmFtZSI6IkNvbGxhdGVyYWwiLCJkYXRhVHlwZSI6eyJ0eXBlIjogInN0cmluZyIsIm1pbkxlbmd0aCI6MX0sInZhbGlkYXRlT25TYWxlIjogInJlcXVpcmVVaW50R3RlIn0sImRlYnQiOnsiZGlzcGxheU5hbWUiOiJEZWJ0IiwiZGF0YVR5cGUiOnsidHlwZSI6ICJzdHJpbmciLCJtaW5MZW5ndGgiOjF9LCJ2YWxpZGF0ZU9uU2FsZSI6ICJyZXF1aXJlVWludEx0ZSJ9fX0='
)
)
);
}

function test_setTraitValues_Revert(uint256 _tokenId, bytes32 _traitKey, bytes32 _traitValue) public {
Expand Down
54 changes: 27 additions & 27 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@opendollar/contracts@^0.0.0-e31c2151":
version "0.0.0-e31c2151"
resolved "https://registry.yarnpkg.com/@opendollar/contracts/-/contracts-0.0.0-e31c2151.tgz#d96fea17c8f865f6b35c74a80d0e95cdfed10943"
integrity sha512-mffuIIDH6YV7gxEbUQghPtKvCamcFoFG4YWnWozjtDec/vrusm2TCjrAKvzV9v8+Sha0dkr6ROryfyMxZIH+IQ==
"@opendollar/contracts@^0.0.0-04ddd4b0":
version "0.0.0-04ddd4b0"
resolved "https://registry.yarnpkg.com/@opendollar/contracts/-/contracts-0.0.0-04ddd4b0.tgz#210042d26d2d79c412a38646bffeacd62797b93b"
integrity sha512-Jn15R/fOXZi50PY0qw86O6ahNkn/AB7k3Rf0gMKI32myoPEyj5gNSFEiumsSkYWsbMmrhMVXPUj5NqSE+4V8aw==
dependencies:
"@defi-wonderland/solidity-utils" "0.0.0-4298c6c6"
"@openzeppelin/contracts" "4.9.6"
Expand Down Expand Up @@ -285,12 +285,12 @@ brace-expansion@^2.0.1:
dependencies:
balanced-match "^1.0.0"

braces@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
braces@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.0.1"
fill-range "^7.1.1"

cacheable-lookup@^7.0.0:
version "7.0.0"
Expand Down Expand Up @@ -725,10 +725,10 @@ file-entry-cache@^5.0.1:
dependencies:
flat-cache "^2.0.1"

fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"

Expand All @@ -746,14 +746,14 @@ flatted@^2.0.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==

"forge-std@git+https://github.com/foundry-rs/forge-std.git":
version "1.8.2"
resolved "git+https://github.com/foundry-rs/forge-std.git#52715a217dc51d0de15877878ab8213f6cbbbab5"

"forge-std@git+https://github.com/foundry-rs/forge-std.git#e8a047e3f40f13fa37af6fe14e6e06283d9a060e":
version "1.5.6"
resolved "git+https://github.com/foundry-rs/forge-std.git#e8a047e3f40f13fa37af6fe14e6e06283d9a060e"

"forge-std@https://github.com/foundry-rs/forge-std":
version "1.8.2"
resolved "https://github.com/foundry-rs/forge-std#52715a217dc51d0de15877878ab8213f6cbbbab5"

form-data-encoder@^2.1.2:
version "2.1.4"
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5"
Expand Down Expand Up @@ -1095,11 +1095,11 @@ merge2@^1.3.0, merge2@^1.4.1:
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==

micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
version "4.0.7"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5"
integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==
dependencies:
braces "^3.0.2"
braces "^3.0.3"
picomatch "^2.3.1"

mimic-fn@^1.0.0:
Expand Down Expand Up @@ -1265,9 +1265,9 @@ path-type@^4.0.0:
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==

picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
version "1.0.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==

picomatch@^2.3.1:
version "2.3.1"
Expand Down Expand Up @@ -1430,9 +1430,9 @@ semver@^6.3.0:
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==

semver@^7.3.7, semver@^7.5.2, semver@^7.6.0:
version "7.6.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.1.tgz#60bfe090bf907a25aa8119a72b9f90ef7ca281b2"
integrity sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==
version "7.6.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==

shebang-command@^1.2.0:
version "1.2.0"
Expand Down
Loading