Skip to content
This repository has been archived by the owner on Nov 27, 2024. It is now read-only.

Commit

Permalink
fix: update LlamaPolicyMetadata to include correct URLs (#484)
Browse files Browse the repository at this point in the history
**Motivation:**

We decided to change our URL schema for the application so we needed to
update the NFT metadata in `LlamaPolicyMetadata`.

**Modifications:**

Made the copy changes, URL changes, and needed to pass the executor
address in `getTokenURI()` in `LlamaPolicy`.

**Result:**

The URLs in the NFT metadata will work correctly.
  • Loading branch information
AustinGreen authored Aug 18, 2023
1 parent 77d065d commit 19393a0
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/LlamaPolicy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ contract LlamaPolicy is ERC721NonTransferableMinimalProxy {
/// @return The token URI for the given `tokenId` of this Llama instance.
function tokenURI(uint256 tokenId) public view override returns (string memory) {
ownerOf(tokenId); // ensure token exists, will revert with NOT_MINTED error if not
return llamaPolicyMetadata.getTokenURI(name, tokenId);
return llamaPolicyMetadata.getTokenURI(name, llamaExecutor, tokenId);
}

/// @notice Returns a URI for the storefront-level metadata for your contract.
Expand Down
27 changes: 18 additions & 9 deletions src/LlamaPolicyMetadata.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ contract LlamaPolicyMetadata is ILlamaPolicyMetadata, Initializable {
}

/// @inheritdoc ILlamaPolicyMetadata
function getTokenURI(string memory name, uint256 tokenId) external view returns (string memory) {
function getTokenURI(string memory name, address executor, uint256 tokenId) external view returns (string memory) {
string[21] memory parts;
string memory policyholder = LibString.toHexString(address(uint160(tokenId)));
string memory truncatedAddress =
Expand Down Expand Up @@ -104,6 +104,9 @@ contract LlamaPolicyMetadata is ILlamaPolicyMetadata, Initializable {
string memory output2 =
string.concat(parts[9], parts[10], parts[11], parts[12], parts[13], parts[14], parts[15], parts[16], parts[17]);
string memory output = string.concat(output1, output2, parts[18], parts[19], parts[20]);
string memory instanceUrl = string.concat(
"https://app.llama.xyz/orgs/", LibString.toString(block.chainid), ":", LibString.toHexString(executor)
);

string memory json = Base64.encode(
bytes(
Expand All @@ -112,9 +115,13 @@ contract LlamaPolicyMetadata is ILlamaPolicyMetadata, Initializable {
truncatedAddress,
' Policy", "description": "This NFT represents membership in the Llama instance: ',
LibString.escapeJSON(name),
". The owner of this NFT can participate in governance according to their roles and permissions. Visit https://app.llama.xyz/profiles/",
". The owner of this NFT can participate in governance according to their roles and permissions. Visit ",
instanceUrl,
"/policies/",
policyholder,
' to view their profile page.", "external_url": "https://app.llama.xyz", "image": "data:image/svg+xml;base64,',
' to see more details.", "external_url": "',
instanceUrl,
'", "image": "data:image/svg+xml;base64,',
Base64.encode(bytes(output)),
'"}'
)
Expand All @@ -127,17 +134,19 @@ contract LlamaPolicyMetadata is ILlamaPolicyMetadata, Initializable {

/// @inheritdoc ILlamaPolicyMetadata
function getContractURI(string memory name, address executor) external view returns (string memory) {
string memory instanceUrl = string.concat(
"https://app.llama.xyz/orgs/", LibString.toString(block.chainid), ":", LibString.toHexString(executor)
);
string[9] memory parts;
parts[0] = '{ "name": "Llama Policies: ';
parts[1] = LibString.escapeJSON(name);
parts[2] = '", "description": "This collection includes all members of the Llama instance: ';
parts[3] = LibString.escapeJSON(name);
parts[4] = ". Visit https://app.llama.xyz/";
parts[5] = LibString.toString(block.chainid);
parts[6] = "/";
parts[7] = LibString.toHexString(executor);
parts[8] =
' to learn more.", "image":"https://llama.xyz/policy-nft/llama-profile.png", "external_link": "https://app.llama.xyz", "banner":"https://llama.xyz/policy-nft/llama-banner.png" }';
parts[4] = ". Visit ";
parts[5] = instanceUrl;
parts[6] = ' to learn more.", "image":"https://llama.xyz/policy-nft/llama-profile.png", "external_link": "';
parts[7] = instanceUrl;
parts[8] = '", "banner":"https://llama.xyz/policy-nft/llama-banner.png" }';
string memory json = Base64.encode(
bytes(string.concat(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]))
);
Expand Down
3 changes: 2 additions & 1 deletion src/interfaces/ILlamaPolicyMetadata.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ interface ILlamaPolicyMetadata {

/// @notice Returns the token URI for a given Llama policy ID.
/// @param name The name of the Llama instance.
/// @param executor The executor of the Llama instance.
/// @param tokenId The token ID of the Llama policyholder.
function getTokenURI(string memory name, uint256 tokenId) external view returns (string memory);
function getTokenURI(string memory name, address executor, uint256 tokenId) external view returns (string memory);

/// @notice Returns the contract URI for a Llama instance's policies.
/// @param name The name of the Llama instance.
Expand Down
59 changes: 41 additions & 18 deletions test/LlamaPolicy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1245,13 +1245,18 @@ contract PolicyMetadata is LlamaPolicyTest {
Metadata memory metadata = parseMetadata(uri);
string memory name = LibString.concat(truncatedAddress, " Policy");
string memory policyholder = LibString.toHexString(address(this));
string memory instanceUrl = string.concat(
"https://app.llama.xyz/orgs/", LibString.toString(block.chainid), ":", LibString.toHexString(address(mpExecutor))
);
string memory description1 =
LibString.concat("This NFT represents membership in the Llama instance: ", LibString.escapeJSON(mpPolicy.name()));
string memory description = string.concat(
description1,
". The owner of this NFT can participate in governance according to their roles and permissions. Visit https://app.llama.xyz/profiles/",
". The owner of this NFT can participate in governance according to their roles and permissions. Visit ",
instanceUrl,
"/policies/",
policyholder,
" to view their profile page."
" to see more details."
);

assertEq(metadata.description, description);
Expand All @@ -1266,7 +1271,7 @@ contract PolicyMetadata is LlamaPolicyTest {
uint256 tokenId = uint256(uint160(policyholder));

string memory name = "Mock Protocol Llama";
assertEq(mpPolicy.tokenURI(tokenId), mpPolicyMetadata.getTokenURI(name, tokenId));
assertEq(mpPolicy.tokenURI(tokenId), mpPolicyMetadata.getTokenURI(name, address(mpExecutor), tokenId));
}

function test_ReturnsCorrectTokenURIEscapesJson() public {
Expand All @@ -1286,13 +1291,21 @@ contract PolicyMetadata is LlamaPolicyTest {
Metadata memory metadata = parseMetadata(uri);
string memory name = LibString.concat(truncatedAddress, " Policy");
string memory policyholder = LibString.toHexString(address(this));
string memory instanceUrl = string.concat(
"https://app.llama.xyz/orgs/",
LibString.toString(block.chainid),
":",
LibString.toHexString(address(deployedExecutor))
);
string memory description1 =
LibString.concat("This NFT represents membership in the Llama instance: ", nameWithQuotes);
string memory description = string.concat(
description1,
". The owner of this NFT can participate in governance according to their roles and permissions. Visit https://app.llama.xyz/profiles/",
". The owner of this NFT can participate in governance according to their roles and permissions. Visit ",
instanceUrl,
"/policies/",
policyholder,
" to view their profile page."
" to see more details."
);

assertEq(LibString.escapeJSON(metadata.description), description);
Expand Down Expand Up @@ -1332,30 +1345,35 @@ contract PolicyMetadataExternalUrl is LlamaPolicyTest {
}

function test_ReturnsCorrectExternalUrl() public {
string memory external_url = string.concat(
"https://app.llama.xyz/orgs/", LibString.toString(block.chainid), ":", LibString.toHexString(address(mpExecutor))
);

vm.prank(address(mpExecutor));
mpPolicy.setRoleHolder(uint8(Roles.TestRole1), address(this), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION);

string memory uri = mpPolicy.tokenURI(uint256(uint160(address(this))));
Metadata memory metadata = parseMetadata(uri);
string memory external_url = "https://app.llama.xyz";
assertEq(metadata.external_url, external_url);
}
}

contract PolicyMetadataContractURI is LlamaPolicyTest {
function test_ReturnsCorrectContractURI() external {
string memory instanceUrl = string.concat(
"https://app.llama.xyz/orgs/", LibString.toString(block.chainid), ":", LibString.toHexString(address(mpExecutor))
);
string memory name = "Mock Protocol Llama";
string[9] memory parts;
parts[0] = '{ "name": "Llama Policies: ';
parts[1] = LibString.escapeJSON(name);
parts[2] = '", "description": "This collection includes all members of the Llama instance: ';
parts[3] = LibString.escapeJSON(name);
parts[4] = ". Visit https://app.llama.xyz/";
parts[5] = LibString.toString(block.chainid);
parts[6] = "/";
parts[7] = LibString.toHexString(address(mpExecutor));
parts[8] =
' to learn more.", "image":"https://llama.xyz/policy-nft/llama-profile.png", "external_link": "https://app.llama.xyz", "banner":"https://llama.xyz/policy-nft/llama-banner.png" }';
parts[4] = ". Visit ";
parts[5] = instanceUrl;
parts[6] = ' to learn more.", "image":"https://llama.xyz/policy-nft/llama-profile.png", "external_link": "';
parts[7] = instanceUrl;
parts[8] = '", "banner":"https://llama.xyz/policy-nft/llama-banner.png" }';
string memory json = Base64.encode(
bytes(string.concat(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]))
);
Expand All @@ -1367,18 +1385,23 @@ contract PolicyMetadataContractURI is LlamaPolicyTest {
(LlamaCore deployedInstance) = deployLlamaWithQuotesInName();
LlamaPolicy deployedPolicy = deployedInstance.policy();
string memory escapedName = '\\"name\\": \\"Mock Protocol Llama\\"';
string memory instanceUrl = string.concat(
"https://app.llama.xyz/orgs/",
LibString.toString(block.chainid),
":",
LibString.toHexString(address(deployedInstance.executor()))
);

string[9] memory parts;
parts[0] = '{ "name": "Llama Policies: ';
parts[1] = escapedName;
parts[2] = '", "description": "This collection includes all members of the Llama instance: ';
parts[3] = escapedName;
parts[4] = ". Visit https://app.llama.xyz/";
parts[5] = LibString.toString(block.chainid);
parts[6] = "/";
parts[7] = LibString.toHexString(address(deployedInstance.executor()));
parts[8] =
' to learn more.", "image":"https://llama.xyz/policy-nft/llama-profile.png", "external_link": "https://app.llama.xyz", "banner":"https://llama.xyz/policy-nft/llama-banner.png" }';
parts[4] = ". Visit ";
parts[5] = instanceUrl;
parts[6] = ' to learn more.", "image":"https://llama.xyz/policy-nft/llama-profile.png", "external_link": "';
parts[7] = instanceUrl;
parts[8] = '", "banner":"https://llama.xyz/policy-nft/llama-banner.png" }';
string memory json = Base64.encode(
bytes(string.concat(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]))
);
Expand Down
4 changes: 3 additions & 1 deletion test/script/DeployLlamaFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ contract Run is DeployLlamaFactoryTest {
DeployLlamaFactory.run();

assertFalse(address(policyMetadataLogic) == address(0));
assertFalse(keccak256(abi.encode(policyMetadataLogic.getTokenURI("MyLlama", 42))) == keccak256(abi.encode("")));
assertFalse(
keccak256(abi.encode(policyMetadataLogic.getTokenURI("MyLlama", address(0), 42))) == keccak256(abi.encode(""))
);
}

function test_DeploysLens() public {
Expand Down

0 comments on commit 19393a0

Please sign in to comment.