Mix.install([
:ethers,
:kino,
:req
])
Application.put_env(:ethereumex, :url, "https://cloudflare-eth.com/v1/mainnet")
defmodule Loader do
def load("ipfs://" <> id) do
Req.get!("https://ipfs.io/ipfs/#{id}").body
end
def load("http" <> _ = url) do
Req.get!(url).body
end
end
{:module, Loader, <<70, 79, 82, 49, 0, 0, 8, ...>>, {:load, 1}}
ERC-721 Tokens have a metadata standard which can be found in this link. This standard enforces a structure to the JSON value inside the token URI. (Token URI can be any valid URI and not necessarily an HTTP(s) URL)
contract_address = "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"
token_id = 10
10
Ethers.Contracts.ERC721.name()
|> Ethers.call!(to: contract_address)
"BoredApeYachtClub"
Ethers.Contracts.ERC721.symbol()
|> Ethers.call!(to: contract_address)
"BAYC"
token_uri =
Ethers.Contracts.ERC721.token_uri(token_id)
|> Ethers.call!(to: contract_address)
"ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/10"
token_data = Loader.load(token_uri)
%{
"attributes" => [
%{"trait_type" => "Clothes", "value" => "Navy Striped Tee"},
%{"trait_type" => "Background", "value" => "Aquamarine"},
%{"trait_type" => "Hat", "value" => "Bayc Hat Red"},
%{"trait_type" => "Fur", "value" => "Dmt"},
%{"trait_type" => "Eyes", "value" => "Eyepatch"},
%{"trait_type" => "Mouth", "value" => "Bored"}
],
"image" => "ipfs://QmPQdVU1riwzijhCs1Lk6CHmDo4LpmwPPLuDauY3i8gSzL"
}
The Json structure in ERC-721 tokens is very simple and conforms to the spec below.
{
"title": "Asset Metadata",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Identifies the asset to which this NFT represents"
},
"description": {
"type": "string",
"description": "Describes the asset to which this NFT represents"
},
"image": {
"type": "string",
"description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
}
}
}
Now we can even load the image.
Loader.load(token_data["image"])
<<137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 2, 119, 0, 0, 2, 119, 8, 6, 0,
0, 0, 246, 202, 119, 98, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11, 19, 0, 0, 11, 19, 1, ...>>
Now we can combine all these calls together for efficiency using Multicall.
calls = [
{Ethers.Contracts.ERC721.name(), to: contract_address},
{Ethers.Contracts.ERC721.symbol(), to: contract_address},
{Ethers.Contracts.ERC721.token_uri(token_id), to: contract_address}
]
Ethers.Multicall.aggregate3(calls)
|> Ethers.call!()
|> Ethers.Multicall.decode(calls)
[
true: "BoredApeYachtClub",
true: "BAYC",
true: "ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/10"
]
ERC-1155 Tokens have a metadata standard which can be found in this link. This standard enforces a structure to the JSON value inside the token URI. (Token URI can be any valid URI and not necessarily an HTTP(s) URL)
erc_1155_contract_address = "0x53894ec021245adb6a7c556bb0f0ad83544c0e33"
erc_1155_token_id = 1
1
token_uri =
Ethers.Contracts.ERC1155.uri(erc_1155_token_id)
|> Ethers.call!(to: erc_1155_contract_address)
"ipfs://bafkreiesnznz43fnowny77t442vhx46b2ksvbid53klekhq5c5rsymzuiq"
token_data = Loader.load(token_uri)
%{
"description" => "Paddy's Pass is the doorway into the UA3 Community where art is our first love, but sharing wins with the community remains a priority! The UA3 ecosystem has been strategically structured through art, fashion, gaming and more to provide strategical one-of-a-kind revenue to the community! Paddy's community wins... While being BORED!",
"image" => "ipfs://bafybeihhjtr2diy7cqmwx74hifobn4qigr3etwio2nfjaznnmdqieausja",
"name" => "UA3 Paddy's Pass"
}
Loader.load(token_data["image"])
<<71, 73, 70, 56, 57, 97, 56, 4, 56, 4, 247, 184, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 3, 1, 0, 5, 2, 1, 5,
4, 3, 7, 5, 3, 10, 6, 2, 10, 7, 6, 11, 7, 4, 13, 10, 9, 16, 9, 2, 16, ...>>
Given a contract address, without prior knowledge we cannot know if that token is of ERC721 or ERC1155 type. This will not be a problem since we can still use all these calls in a Multicall3 aggregation and the unsupported ones can be ignored.
calls = [
{Ethers.Contracts.ERC721.name(), to: erc_1155_contract_address},
{Ethers.Contracts.ERC721.symbol(), to: erc_1155_contract_address},
{Ethers.Contracts.ERC721.token_uri(erc_1155_token_id), to: erc_1155_contract_address},
{Ethers.Contracts.ERC1155.uri(erc_1155_token_id), to: erc_1155_contract_address}
]
Ethers.Multicall.aggregate3(calls)
|> Ethers.call!()
|> Ethers.Multicall.decode(calls)
[
false: nil,
false: nil,
false: nil,
true: "ipfs://bafkreiesnznz43fnowny77t442vhx46b2ksvbid53klekhq5c5rsymzuiq"
]
The above example shows what the response will look like for an ERC1155 token.