Skip to content

Commit

Permalink
Fix metadata (#586)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brean0 committed Aug 4, 2023
2 parents 9d2820c + 40e6b56 commit 72de183
Show file tree
Hide file tree
Showing 16 changed files with 167 additions and 81 deletions.
7 changes: 7 additions & 0 deletions protocol/contracts/beanstalk/init/InitBipBasinIntegration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pragma experimental ABIEncoderV2;
import {AppStorage} from "../AppStorage.sol";
import {C} from "contracts/C.sol";
import {LibWhitelist} from "contracts/libraries/Silo/LibWhitelist.sol";
import {LibDiamond} from "contracts/libraries/LibDiamond.sol";



/**
Expand All @@ -34,6 +36,8 @@ contract InitBipBasinIntegration {


function init() external {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();

LibWhitelist.updateStalkPerBdvPerSeasonForToken(C.BEAN, NEW_BEAN_SEEDS_PER_BDV);
LibWhitelist.updateStalkPerBdvPerSeasonForToken(C.CURVE_BEAN_METAPOOL, NEW_BEAN_3CRV_SEEDS_PER_BDV);
LibWhitelist.whitelistToken(
Expand All @@ -45,5 +49,8 @@ contract InitBipBasinIntegration {
);

s.beanEthPrice = 1;

// adds ERC1155MetadataURI for ERC165 Interface ID
ds.supportedInterfaces[0x0e89341c] = true;
}
}
3 changes: 3 additions & 0 deletions protocol/contracts/beanstalk/init/InitDiamond.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ contract InitDiamond {
ds.supportedInterfaces[type(IERC165).interfaceId] = true;
ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true;
ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true;
ds.supportedInterfaces[0xd9b67a26] = true; // ERC1155
ds.supportedInterfaces[0x0e89341c] = true; // ERC1155Metadata


C.bean().approve(C.CURVE_BEAN_METAPOOL, type(uint256).max);
C.bean().approve(C.curveZapAddress(), type(uint256).max);
Expand Down
39 changes: 27 additions & 12 deletions protocol/contracts/beanstalk/metadata/MetadataFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;

import {LibLegacyTokenSilo} from "contracts/libraries/Silo/LibLegacyTokenSilo.sol";
import "./MetadataImage.sol";
import "contracts/mocks/MockERC1155.sol";
import {LibBytes} from "contracts/libraries/LibBytes.sol";
import {LibTokenSilo} from "contracts/libraries/Silo/LibTokenSilo.sol";


/**
Expand All @@ -19,6 +21,10 @@ import "./MetadataImage.sol";
*/
contract MetadataFacet is MetadataImage {
using LibStrings for uint256;
using LibStrings for int256;


event URI(string _uri, uint256 indexed _id);

/**
* @notice Returns the URI for a given depositId.
Expand All @@ -27,26 +33,35 @@ contract MetadataFacet is MetadataImage {
* Deposits are stored as a mapping of a uint256 to a Deposit struct.
* ERC20 deposits are represented by the concatination of the token address and the stem. (20 + 12 bytes).
*/
function uri(uint256 depositId) external view returns (string memory) {
function uri(uint256 depositId) public view returns (string memory) {
(address token, int96 stem) = LibBytes.unpackAddressAndStem(depositId);
int96 stemTip = LibTokenSilo.stemTipForToken(token);
require(token != address(0), "Silo: metadata does not exist");
bytes memory attributes = abi.encodePacked(
'\n\nToken Symbol: ', getTokenName(token),
'\nToken Address: ', LibStrings.toHexString(uint256(token), 20),
'\nId: ', depositId.toHexString(32),
'\nDeposit stem: ', uint256(stem).toString(),
'\nDeposit inital stalk per BDV: ', uint256(LibTokenSilo.stalkIssuedPerBdv(token)).toString(),
'\nDeposit grown stalk per BDV": ', uint256(LibTokenSilo.stemTipForToken(token) - stem).toString(),
'\nDeposit seeds per BDV": ', uint256(LibLegacyTokenSilo.getSeedsPerToken(token)).toString(),
'\n\nDISCLAIMER: Due diligence is imperative when assessing this NFT. Opensea and other NFT marketplaces cache the svg output and thus, may require the user to refresh the metadata to properly show the correct values."'
'\\n\\nToken Symbol: ', getTokenName(token),
'\\nToken Address: ', LibStrings.toHexString(uint256(token), 20),
'\\nId: ', depositId.toHexString(32),
'\\nstem: ', int256(stem).toString(),
'\\ninital stalk per BDV: ', uint256(LibTokenSilo.stalkIssuedPerBdv(token)).toString(),
'\\ngrown stalk per BDV: ', uint256(stemTip - stem).toString(),
'\\nstalk grown per BDV per season: ', uint256(LibTokenSilo.stalkEarnedPerSeason(token)).toString(),
'\\n\\nDISCLAIMER: Due diligence is imperative when assessing this NFT. Opensea and other NFT marketplaces cache the svg output and thus, may require the user to refresh the metadata to properly show the correct values."'
);
return string(abi.encodePacked("data:application/json;base64,",LibBytes64.encode(abi.encodePacked(
'{',
'"name": "Beanstalk Deposit", "description": "A Beanstalk Deposit.',
'"name": "Beanstalk Silo Deposits", "description": "An ERC1155 representing an asset deposited in the Beanstalk Silo. Silo Deposits gain stalk and bean seignorage.',
attributes,
string(abi.encodePacked(', "image": "', imageURI(depositId), '"')),
string(abi.encodePacked(', "image": "', imageURI(token, stem, stemTip), '"')),
'}'
))
));
}

function name() external pure returns (string memory){
return "Beanstalk Silo Deposits";
}

function symbol() external pure returns (string memory){
return "DEPOSIT";
}
}
106 changes: 94 additions & 12 deletions protocol/contracts/beanstalk/metadata/MetadataImage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

pragma solidity ^0.7.6;
import "../AppStorage.sol";
import {LibTokenSilo} from "contracts/libraries/Silo/LibTokenSilo.sol";
import {LibBytes} from "contracts/libraries/LibBytes.sol";
import {LibBytes64} from "contracts/libraries/LibBytes64.sol";
import {LibStrings} from "contracts/libraries/LibStrings.sol";
import {C} from "../../C.sol";
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {C} from "../../C.sol";


/**
Expand All @@ -21,20 +19,24 @@ contract MetadataImage {
AppStorage internal s;

using LibStrings for uint256;
using LibStrings for int256;
using SafeMath for uint256;

string constant LEAF_COLOR_0 = '#A8C83A';
string constant LEAF_COLOR_1 = '#89A62F';
uint256 constant NUM_PLOTS = 21;
uint256 constant STALK_GROWTH = 2e2;

function imageURI(uint256 depositId) public view returns (string memory){
return string(abi.encodePacked("data:image/svg+xml;base64,", LibBytes64.encode(bytes(generateImage(depositId)))));
function imageURI(address token, int96 stem, int96 stemTip) public view returns (string memory){
return string(
abi.encodePacked(
"data:image/svg+xml;base64,",
LibBytes64.encode(bytes(generateImage(token, stem, stemTip)))
)
);
}

function generateImage(uint256 depositId) internal view returns (string memory) {
(address token, int96 stem) = LibBytes.unpackAddressAndStem(depositId);
int96 stemTip = LibTokenSilo.stemTipForToken(token);
function generateImage(address token, int96 stem, int96 stemTip) internal view returns (string memory) {
int96 grownStalkPerBdv = stemTip - stem;
return string(
abi.encodePacked(
Expand Down Expand Up @@ -524,13 +526,93 @@ contract MetadataImage {
useAsset(getTokenName(token), 240, 4),
'<rect x="0" y="330" width="255" height="20" rx="5" fill="#242424"/>',
movingTokenAddress(token),
'<text x="230" y="14.5" font-size="12" fill="White" text-anchor="end" font-family="futura">Stem: ',
uint256(stem).toString(),
'<text x="235" y="14.5" font-size="12" fill="White" text-anchor="end" font-family="futura">Stem: ',
SciNotation(stem),
'</text>'
)
);
}

function SciNotation(int96 stem) internal pure returns (string memory) {
if(stem >= 0){
// if stem is greater than 1e7, use scientific notation
if(stem > 100_000){
return powerOfTen(uint256(stem));
} else {
return uint256(stem).toString();
}
} else {
// if stem is greater than 1e7, use scientific notation
if(-stem > 100_000){
return string(abi.encodePacked("-", powerOfTen(uint256(-stem))));
} else {
return int256(stem).toString();
}
}
}

function powerOfTen(uint256 stem) internal pure returns (string memory) {
// if else ladder to determine how many digits to show
if(stem < 1e6){
return stemDecimals(stem, 5);
} else if(stem < 1e7) {
return stemDecimals(stem, 6);
} else if(stem < 1e8) {
return stemDecimals(stem, 7);
} else if(stem < 1e9) {
return stemDecimals(stem, 8);
} else if(stem < 1e10) {
return stemDecimals(stem, 9);
} else if(stem < 1e11) {
return stemDecimals(stem, 10);
} else if(stem < 1e12) {
return stemDecimals(stem, 11);
} else if(stem < 1e13) {
return stemDecimals(stem, 12);
} else if(stem < 1e14) {
return stemDecimals(stem, 13);
} else if(stem < 1e15) {
return stemDecimals(stem, 14);
} else if(stem < 1e16) {
return stemDecimals(stem, 15);
} else if(stem < 1e17) {
return stemDecimals(stem, 16);
} else if(stem < 1e18) {
return stemDecimals(stem, 17);
} else if(stem < 1e19) {
return stemDecimals(stem, 18);
} else if(stem < 1e20) {
return stemDecimals(stem, 19);
} else if(stem < 1e21) {
return stemDecimals(stem, 20);
} else if(stem < 1e22) {
return stemDecimals(stem, 21);
} else if(stem < 1e23) {
return stemDecimals(stem, 22);
} else if(stem < 1e24) {
return stemDecimals(stem, 23);
} else if(stem < 1e25) {
return stemDecimals(stem, 24);
} else if(stem < 1e26) {
return stemDecimals(stem, 25);
} else if(stem < 1e27) {
return stemDecimals(stem, 26);
} else if(stem < 1e28) {
return stemDecimals(stem, 27);
} else {
return stemDecimals(stem, 28);
}
}
function stemDecimals(uint256 stem, uint256 exponent) internal pure returns (string memory) {
return string(abi.encodePacked(
stem.div(10 ** exponent).toString(),
'.',
stem.div(10 ** exponent.sub(5)).mod(1e5).toString(),
'e',
exponent.toString()
));
}

function tokenName(address token) internal pure returns (string memory) {
return string(
abi.encodePacked(
Expand All @@ -547,14 +629,14 @@ contract MetadataImage {
'<text x="127" y="343" font-size="10" fill="White" text-anchor="middle" font-family="futura">',
'<tspan><animate attributeName="x" from="375" to="50" dur="10s" repeatCount="indefinite" />',
LibStrings.toHexString(token),
'</tspan></text>'
'</tspan></text>',
'<text x="127" y="343" font-size="10" fill="White" text-anchor="middle" font-family="futura">',
'<tspan><animate attributeName="x" from="50" to="-275" dur="10s" repeatCount="indefinite" />',
LibStrings.toHexString(token),
'</tspan></text>'
)
);
}
}

function intToStr(int256 x) internal pure returns (string memory) {
if(x < 0){
Expand Down
11 changes: 11 additions & 0 deletions protocol/contracts/libraries/LibStrings.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,15 @@ library LibStrings {
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}

/**
* @dev Converts a `int256` to its ASCII `string` representation.
*/
function toString(int256 value) internal pure returns(string memory){
if(value > 0){
return toString(uint256(value));
} else {
return string(abi.encodePacked("-", toString(uint256(-value))));
}
}
}
2 changes: 2 additions & 0 deletions protocol/contracts/mocks/mockFacets/MockSeasonFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ contract MockSeasonFacet is SeasonFacet {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();

ds.supportedInterfaces[type(IERC1155).interfaceId] = true;
ds.supportedInterfaces[0x0e89341c] = true;


uint32 currentSeason = s.season.current;

Expand Down
8 changes: 7 additions & 1 deletion protocol/hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,13 @@ module.exports = {
chainId: 31337,
url: "https://rpc.vnet.tenderly.co/devnet/silo-v3/3ed19e82-a81c-45e5-9b16-5e385aa74587",
timeout: 100000
}
},
goerli: {
chainId: 5,
url: process.env.GOERLI_RPC || "",
accounts: [process.env.GOERLI_PRIVATE_KEY],
timeout: 100000
},
},
etherscan: {
apiKey: process.env.ETHERSCAN_KEY
Expand Down
46 changes: 0 additions & 46 deletions protocol/test/EthUsdOracleAh.test.js

This file was deleted.

11 changes: 8 additions & 3 deletions protocol/test/Silo.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ describe('Silo', function () {

it("properly gives an URI", async function () {
await this.season.farmSunrises(1000);

depositmetadata = await fs.readFileSync(__dirname + '/data/base64EncodedImageBean.txt', 'utf-8');
depositID1 = '0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab000000000000000000000002';
expect(await this.metadata.uri(depositID1)).to.eq(depositmetadata);
Expand All @@ -375,17 +375,22 @@ describe('Silo', function () {
expect(await this.metadata.uri(depositID3)).to.eq(depositmetadata);

depositmetadata = await fs.readFileSync(__dirname + '/data/base64EncodedImageUrBean3Crv.txt', 'utf-8');
depositID4 = '0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716D000000000000000000000684';
depositID4 = '0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716DFFFFFFFFFFFFFFFFFFFFF97C';
expect(await this.metadata.uri(depositID4)).to.eq(depositmetadata);

depositmetadata = await fs.readFileSync(__dirname + '/data/base64EncodedImageUrBean3Crv2.txt', 'utf-8');
depositID4 = '0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716DFFF000000000000000000111';
expect(await this.metadata.uri(depositID4)).to.eq(depositmetadata);

depositmetadata = await fs.readFileSync(__dirname + '/data/base64EncodedImageBeanEth.txt', 'utf-8');
depositID5 = '0x9bAaB117304f7D6517048e371025dB8f89a8DbE5000000000000000000000002';
depositID5 = '0x9bAaB117304f7D6517048e371025dB8f89a8DbE5FFFFFFFFFFFFF00000000002';
expect(await this.metadata.uri(depositID5)).to.eq(depositmetadata);

});

it("properly gives the correct ERC-165 identifier", async function () {
expect(await this.diamondLoupe.supportsInterface("0xd9b67a26")).to.eq(true);
expect(await this.diamondLoupe.supportsInterface("0x0e89341c")).to.eq(true);
});
});

Expand Down
Loading

0 comments on commit 72de183

Please sign in to comment.