From 7a1176b73ccda2813cb3f24401f3b200e22b4a6f Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:07:32 +0100 Subject: [PATCH 1/7] SIP-29: Snap Assets API --- SIPS/sip-29.md | 196 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 SIPS/sip-29.md diff --git a/SIPS/sip-29.md b/SIPS/sip-29.md new file mode 100644 index 0000000..b52521b --- /dev/null +++ b/SIPS/sip-29.md @@ -0,0 +1,196 @@ +--- +sip: (To be assigned) +title: Snap Assets API +status: Draft +discussions-to (*optional): (Http/Https URL) +author: Daniel Rocha (@danroc), Guillaume Roux (@GuillaumeRx) +created: (Date created on in ISO 8601 format. `yyyy-mm-dd`) +updated (*optional): (Date last updated on in https://en.wikipedia.org/wiki/ISO_8601 format. `yyyy-mm-dd`. This should be only used on SIPs with `Living` status) +--- + +## Abstract + +This SIP aims to define a new API that can be exposed by Snaps to allow clients +get asset information in a chain-agnostic way. + +## Motivation + +To allow clients to be chain-agnostic, the logic of how to get asset +information should be abstracted away from the client. We also need to define +the types that represent the asset information required by the clients. + +## Specification + +> Indented sections like this are considered non-normative. + +### Language + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", +"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" written +in uppercase in this document are to be interpreted as described in [RFC +2119](https://www.ietf.org/rfc/rfc2119.txt) + +### Definitions + +In this document, all definitions are written in TypeScript. Also, any time an +asset need to be identified, it MUST use the [CAIP-19][caip-19] representation. + +### Snap Assets API + +Two methods are defined in the Snap Assets API: + +#### Get Token Description + +```typescript +// Represents a token unit. +type TokenUnit { + // Human-friendly name of the token unit. + name: string; + + // Ticker of the token unit. + ticker: string; + + // Number of decimals of the token unit. + decimals: number; +} + +// Token description. +type TokenDescription { + // Human-friendly name of the token. + name: string; + + // Ticker of the token. + ticker: string; + + // Whether the token is native to the chain. + isNative: boolean; + + // Base64 representation of the token icon. + iconBase64: string; + + // List of token units. + units: TokenUnit[]; +} + +// Returns the description of a non-fungible token. This description can then +// be used by the client to display relevant information about the token. +// +// @example +// ```typescript +// const tokenDescription = await getTokenDescription('eip155:1/slip44:60'); +// +// // Returns: +// // { +// // name: 'Ether', +// // ticker: 'ETH', +// // isNative: true, +// // iconBase64: 'data:image/png;base64,...', +// // units: [ +// // { +// // name: 'Ether', +// // ticker: 'ETH', +// // decimals: 18 +// // }, +// // { +// // name: 'Gwei', +// // ticker: 'Gwei', +// // decimals: 9 +// // }, +// // { +// // name: 'wei', +// // ticker: 'wei', +// // decimals: 0 +// // } +// // ] +// ``` +function getTokenDescription(token: Caip19AssetType): TokenDescription; +``` + +#### Get Token Conversion Rate + +```typescript +type TokenConversionRate { + // The rate of conversion from the source token to the target token. It + // means that 1 unit of the `from` token should be converted to this amount + // of the `to` token. + rate: string; + + // The UNIX timestamp of when the conversion rate was last updated. + conversionTime: number; + + // The UNIX timestamp of when the conversion rate will expire. + expirationTime: number; +} + +// Returns the conversion rate between two assets (tokens or fiat). +// +// @example +// ```typescript +// const conversionRate = await getTokenConversionRate( +// 'eip155:1/slip44:60', +// 'eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f' +// ); +// +// // Returns: +// // { +// // rate: '3906.38', +// // conversionTime: 1733389786, +// // expirationTime: 1733389816, +// // } +// ``` +function getTokenConversionRate( + from: Caip19AssetType, + to: Caip19AssetType +): TokenConversionRate; +``` + +### Fiat currency representation + +We SHOULD use CAIP-19 to represent fiat currencies as well, it will allow us to +have a consistent way to represent all assets and make the API more +predictable. + +The propose format is: + +``` +asset_type: chain_id + "/" + asset_namespace + ":" + asset_reference +chain_id: namespace + ":" + reference +namespace: "fiat" +reference: country_code +asset_namespace: "currency" +asset_reference: currency_code +``` + +The country code is a two-letter lowercase code as defined by the ISO 3166-1 +alpha-2, with the exception for the European Union, which is represented by +"eu". + +The currency code is a three-letter uppercase code as defined by the ISO 4217. + +Examples: + +``` +# Euro +fiat:eu/currency:eur + +# United States Dollar +fiat:us/currency:usd + +# Brazilian Real +fiat:br/currency:brl + +# Japanese Yen +fiat:jp/currency:jpy +``` + +## Backwards compatibility + +Any SIPs that break backwards compatibility MUST include a section describing +those incompatibilities and their severity. The SIP SHOULD describe how the +author plans on proposes to deal with such these incompatibilities. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE). + +[caip-19]: https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-19.md From 8f7b7d04ef7cca015aa6f325569b302da4659000 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:13:50 +0100 Subject: [PATCH 2/7] Fix typos --- SIPS/sip-29.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/SIPS/sip-29.md b/SIPS/sip-29.md index b52521b..036831f 100644 --- a/SIPS/sip-29.md +++ b/SIPS/sip-29.md @@ -11,13 +11,13 @@ updated (*optional): (Date last updated on in https://en.wikipedia.org/wiki/ISO_ ## Abstract This SIP aims to define a new API that can be exposed by Snaps to allow clients -get asset information in a chain-agnostic way. +to retrieve asset information in a chain-agnostic way. ## Motivation -To allow clients to be chain-agnostic, the logic of how to get asset -information should be abstracted away from the client. We also need to define -the types that represent the asset information required by the clients. +To enable clients to be chain-agnostic, the logic for obtaining asset +information should be abstracted away from the client. Additionally, this SIP +defines the types that represent the asset information required by clients. ## Specification @@ -32,8 +32,10 @@ in uppercase in this document are to be interpreted as described in [RFC ### Definitions -In this document, all definitions are written in TypeScript. Also, any time an -asset need to be identified, it MUST use the [CAIP-19][caip-19] representation. +1. In this document, all definitions are written in TypeScript. + +2. Any time an asset needs to be identified, it MUST use the [CAIP-19][caip-19] +representation. ### Snap Assets API @@ -43,7 +45,7 @@ Two methods are defined in the Snap Assets API: ```typescript // Represents a token unit. -type TokenUnit { +type TokenUnit = { // Human-friendly name of the token unit. name: string; @@ -52,10 +54,10 @@ type TokenUnit { // Number of decimals of the token unit. decimals: number; -} +}; // Token description. -type TokenDescription { +type TokenDescription = { // Human-friendly name of the token. name: string; @@ -70,7 +72,7 @@ type TokenDescription { // List of token units. units: TokenUnit[]; -} +}; // Returns the description of a non-fungible token. This description can then // be used by the client to display relevant information about the token. @@ -102,6 +104,7 @@ type TokenDescription { // // decimals: 0 // // } // // ] +// // } // ``` function getTokenDescription(token: Caip19AssetType): TokenDescription; ``` @@ -109,7 +112,7 @@ function getTokenDescription(token: Caip19AssetType): TokenDescription; #### Get Token Conversion Rate ```typescript -type TokenConversionRate { +type TokenConversionRate = { // The rate of conversion from the source token to the target token. It // means that 1 unit of the `from` token should be converted to this amount // of the `to` token. @@ -120,7 +123,7 @@ type TokenConversionRate { // The UNIX timestamp of when the conversion rate will expire. expirationTime: number; -} +}; // Returns the conversion rate between two assets (tokens or fiat). // @@ -146,11 +149,9 @@ function getTokenConversionRate( ### Fiat currency representation -We SHOULD use CAIP-19 to represent fiat currencies as well, it will allow us to -have a consistent way to represent all assets and make the API more -predictable. - -The propose format is: +We SHOULD use CAIP-19 to represent fiat currencies as well. This approach +provides a consistent way to represent all assets, making the API more +predictable. The proposed format is: ``` asset_type: chain_id + "/" + asset_namespace + ":" + asset_reference @@ -161,11 +162,10 @@ asset_namespace: "currency" asset_reference: currency_code ``` -The country code is a two-letter lowercase code as defined by the ISO 3166-1 -alpha-2, with the exception for the European Union, which is represented by -"eu". +The country code is a two-letter lowercase code as defined by ISO 3166-1 +alpha-2, with the exception of the European Union, represented by "eu". -The currency code is a three-letter uppercase code as defined by the ISO 4217. +The currency code is a three-letter uppercase code as defined by ISO 4217. Examples: From f0a9807dd09858c9ae9ee5c83588131d387b0c97 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:14:55 +0100 Subject: [PATCH 3/7] Update document header --- SIPS/sip-29.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/SIPS/sip-29.md b/SIPS/sip-29.md index 036831f..60e1d1b 100644 --- a/SIPS/sip-29.md +++ b/SIPS/sip-29.md @@ -1,11 +1,9 @@ --- -sip: (To be assigned) +sip: 29 title: Snap Assets API status: Draft -discussions-to (*optional): (Http/Https URL) author: Daniel Rocha (@danroc), Guillaume Roux (@GuillaumeRx) -created: (Date created on in ISO 8601 format. `yyyy-mm-dd`) -updated (*optional): (Date last updated on in https://en.wikipedia.org/wiki/ISO_8601 format. `yyyy-mm-dd`. This should be only used on SIPs with `Living` status) +created: 2024-12-05 --- ## Abstract From 03b33e5e400e0f0f5fd0cc397c2d2f604e8fbe74 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:16:30 +0100 Subject: [PATCH 4/7] Add precision about emitter country --- SIPS/sip-29.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SIPS/sip-29.md b/SIPS/sip-29.md index 60e1d1b..d531170 100644 --- a/SIPS/sip-29.md +++ b/SIPS/sip-29.md @@ -160,8 +160,9 @@ asset_namespace: "currency" asset_reference: currency_code ``` -The country code is a two-letter lowercase code as defined by ISO 3166-1 -alpha-2, with the exception of the European Union, represented by "eu". +The country code is a two-letter lowercase code, as defined by ISO 3166-1 +alpha-2, representing the emitter country, with the exception of the European +Union, which is represented by "eu". The currency code is a three-letter uppercase code as defined by ISO 4217. From 5712f64cb95fc78a0498f88e65f58d118581843a Mon Sep 17 00:00:00 2001 From: Guillaume Roux Date: Fri, 6 Dec 2024 17:24:01 +0100 Subject: [PATCH 5/7] SIP-29 Improvements (#155) * add handlers and permission definition * update to use `snaps-sdk` * snap -> Snap * Add CAIP-19 ID reference in description and rename token to asset * Add chains caveat * fix typo * allow batching * add fungible reference to asset description * add appendix for fungible assets * fix typos * update rates handler to support batching * unify * rename `AssetUnit` to `FungibleAssetUnit` --- SIPS/sip-29.md | 200 +++++++++++++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 82 deletions(-) diff --git a/SIPS/sip-29.md b/SIPS/sip-29.md index d531170..ea7ee1a 100644 --- a/SIPS/sip-29.md +++ b/SIPS/sip-29.md @@ -35,85 +35,93 @@ in uppercase in this document are to be interpreted as described in [RFC 2. Any time an asset needs to be identified, it MUST use the [CAIP-19][caip-19] representation. -### Snap Assets API +### Snap Manifest + +This SIP introduces a new permission named `endowment:assets`. +This permission grants a Snap the ability to provide asset information to the client. + +This permission is specified as follows in `snap.manifest.json` files: + +```json +{ + "initialPermissions": { + "endowment:assets": { + "scopes": [ + "bip122:000000000019d6689c085ae165831e93" + ] + } + } +} +``` + +`scopes` - A non-empty array of CAIP-2 chain IDs that the snap supports. This field is useful for a client in order to avoid unnecessary overhead. + +### Snap Implementation Two methods are defined in the Snap Assets API: -#### Get Token Description +Any Snap that wishes to provide asset information **MUST** implement the following API: -```typescript -// Represents a token unit. -type TokenUnit = { - // Human-friendly name of the token unit. - name: string; +#### Get Asset Metadata - // Ticker of the token unit. - ticker: string; +```typescript +import { OnAssetLookupHandler } from "@metamask/snaps-sdk"; - // Number of decimals of the token unit. - decimals: number; +export const onAssetLookup: OnAssetLookupHandler = async ({ + assets +}) => { + const assetsMetadata = /* Get metadata */; + return { assets: assetsMetadata }; }; +``` -// Token description. -type TokenDescription = { - // Human-friendly name of the token. - name: string; - - // Ticker of the token. - ticker: string; +The type for an `onAssetLookup` handler function’s arguments is: - // Whether the token is native to the chain. - isNative: boolean; - // Base64 representation of the token icon. - iconBase64: string; +```typescript +interface OnAssetLookupArgs { + assets: Caip19AssetType[]; +} +``` +The type for an `onAssetLookup` handler function’s return value is: - // List of token units. - units: TokenUnit[]; +```typescript +type OnAssetLookupReturn = { + assets: Record; }; +``` + +#### Get Asset Conversion Rate + +```typescript +import { OnAssetConversionHandler } from "@metamask/snaps-sdk"; -// Returns the description of a non-fungible token. This description can then -// be used by the client to display relevant information about the token. -// -// @example -// ```typescript -// const tokenDescription = await getTokenDescription('eip155:1/slip44:60'); -// -// // Returns: -// // { -// // name: 'Ether', -// // ticker: 'ETH', -// // isNative: true, -// // iconBase64: 'data:image/png;base64,...', -// // units: [ -// // { -// // name: 'Ether', -// // ticker: 'ETH', -// // decimals: 18 -// // }, -// // { -// // name: 'Gwei', -// // ticker: 'Gwei', -// // decimals: 9 -// // }, -// // { -// // name: 'wei', -// // ticker: 'wei', -// // decimals: 0 -// // } -// // ] -// // } -// ``` -function getTokenDescription(token: Caip19AssetType): TokenDescription; +export const onAssetConversion: OnAssetConversionHandler = async ({ + conversions +}) => { + const conversionRates = /* Get conversion rate */; + return { conversionRates }; +}; ``` +The type for an `onAssetConversion` handler function’s arguments is: + +```typescript +type Conversion = { + from: Caip19AssetType; + to: Caip19AssetType; +}; -#### Get Token Conversion Rate +type OnAssetConversionArgs = { + conversions: Conversion[]; +} +``` +The type for an `onAssetConversion` handler function’s return value is: ```typescript -type TokenConversionRate = { - // The rate of conversion from the source token to the target token. It - // means that 1 unit of the `from` token should be converted to this amount - // of the `to` token. +type AssetConversionRate = { + // The rate of conversion from the source asset to the target asset. It + // means that 1 unit of the `from` asset should be converted to this amount + // of the `to` asset. rate: string; // The UNIX timestamp of when the conversion rate was last updated. @@ -123,26 +131,13 @@ type TokenConversionRate = { expirationTime: number; }; -// Returns the conversion rate between two assets (tokens or fiat). -// -// @example -// ```typescript -// const conversionRate = await getTokenConversionRate( -// 'eip155:1/slip44:60', -// 'eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f' -// ); -// -// // Returns: -// // { -// // rate: '3906.38', -// // conversionTime: 1733389786, -// // expirationTime: 1733389816, -// // } -// ``` -function getTokenConversionRate( - from: Caip19AssetType, - to: Caip19AssetType -): TokenConversionRate; +type FromAsset = Conversion["from"]; + +type ToAsset = Conversion["to"]; + +type OnAssetConversionReturn = { + conversionRates: Record>; +}; ``` ### Fiat currency representation @@ -182,6 +177,47 @@ fiat:br/currency:brl fiat:jp/currency:jpy ``` +## Appendix I: Fungible Asset Metadata + +The following asset metadata fields for a fungible asset are defined. +As of the time of creation of this SIP, they are the only possible assets requested by clients. + +```typescript +// Represents an asset unit. +type FungibleAssetUnit = { + // Human-friendly name of the asset unit. + name: string; + + // Ticker of the asset unit. + ticker: string; + + // Number of decimals of the asset unit. + decimals: number; +}; + +// Fungible asset metadata. +type FungibleAssetMetadata = { + // Human-friendly name of the asset. + name: string; + + // Ticker of the asset. + ticker: string; + + // Whether the asset is native to the chain. + native: boolean; + + // Represents a fungible asset + fungible: true; + + // Base64 representation of the asset icon. + iconBase64: string; + + // List of asset units. + units: FungibleAssetUnit[]; +}; +``` + + ## Backwards compatibility Any SIPs that break backwards compatibility MUST include a section describing From ec71c691688ae48108079fdb8feb6a8e7b986783 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:26:06 +0100 Subject: [PATCH 6/7] Fix formatting --- SIPS/sip-29.md | 79 +++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/SIPS/sip-29.md b/SIPS/sip-29.md index ea7ee1a..47205e6 100644 --- a/SIPS/sip-29.md +++ b/SIPS/sip-29.md @@ -33,7 +33,7 @@ in uppercase in this document are to be interpreted as described in [RFC 1. In this document, all definitions are written in TypeScript. 2. Any time an asset needs to be identified, it MUST use the [CAIP-19][caip-19] -representation. + representation. ### Snap Manifest @@ -46,9 +46,9 @@ This permission is specified as follows in `snap.manifest.json` files: { "initialPermissions": { "endowment:assets": { - "scopes": [ - "bip122:000000000019d6689c085ae165831e93" - ] + "scopes": [ + "bip122:000000000019d6689c085ae165831e93" + ] } } } @@ -77,17 +77,17 @@ export const onAssetLookup: OnAssetLookupHandler = async ({ The type for an `onAssetLookup` handler function’s arguments is: - ```typescript interface OnAssetLookupArgs { - assets: Caip19AssetType[]; + assets: Caip19AssetType[]; } ``` + The type for an `onAssetLookup` handler function’s return value is: ```typescript type OnAssetLookupReturn = { - assets: Record; + assets: Record; }; ``` @@ -103,32 +103,34 @@ export const onAssetConversion: OnAssetConversionHandler = async ({ return { conversionRates }; }; ``` + The type for an `onAssetConversion` handler function’s arguments is: ```typescript type Conversion = { - from: Caip19AssetType; - to: Caip19AssetType; + from: Caip19AssetType; + to: Caip19AssetType; }; type OnAssetConversionArgs = { - conversions: Conversion[]; -} + conversions: Conversion[]; +}; ``` + The type for an `onAssetConversion` handler function’s return value is: ```typescript type AssetConversionRate = { - // The rate of conversion from the source asset to the target asset. It - // means that 1 unit of the `from` asset should be converted to this amount - // of the `to` asset. - rate: string; + // The rate of conversion from the source asset to the target asset. It + // means that 1 unit of the `from` asset should be converted to this amount + // of the `to` asset. + rate: string; - // The UNIX timestamp of when the conversion rate was last updated. - conversionTime: number; + // The UNIX timestamp of when the conversion rate was last updated. + conversionTime: number; - // The UNIX timestamp of when the conversion rate will expire. - expirationTime: number; + // The UNIX timestamp of when the conversion rate will expire. + expirationTime: number; }; type FromAsset = Conversion["from"]; @@ -136,7 +138,7 @@ type FromAsset = Conversion["from"]; type ToAsset = Conversion["to"]; type OnAssetConversionReturn = { - conversionRates: Record>; + conversionRates: Record>; }; ``` @@ -185,39 +187,38 @@ As of the time of creation of this SIP, they are the only possible assets reques ```typescript // Represents an asset unit. type FungibleAssetUnit = { - // Human-friendly name of the asset unit. - name: string; + // Human-friendly name of the asset unit. + name: string; - // Ticker of the asset unit. - ticker: string; + // Ticker of the asset unit. + ticker: string; - // Number of decimals of the asset unit. - decimals: number; + // Number of decimals of the asset unit. + decimals: number; }; // Fungible asset metadata. type FungibleAssetMetadata = { - // Human-friendly name of the asset. - name: string; + // Human-friendly name of the asset. + name: string; - // Ticker of the asset. - ticker: string; + // Ticker of the asset. + ticker: string; - // Whether the asset is native to the chain. - native: boolean; + // Whether the asset is native to the chain. + native: boolean; - // Represents a fungible asset - fungible: true; + // Represents a fungible asset + fungible: true; - // Base64 representation of the asset icon. - iconBase64: string; + // Base64 representation of the asset icon. + iconBase64: string; - // List of asset units. - units: FungibleAssetUnit[]; + // List of asset units. + units: FungibleAssetUnit[]; }; ``` - ## Backwards compatibility Any SIPs that break backwards compatibility MUST include a section describing From 5657a9c97467860f5502ae64066fcfd6308544e6 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:50:48 +0100 Subject: [PATCH 7/7] Rename `ticker` to `symbol` It should be closer to the terminology used by the Token API. --- SIPS/sip-29.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SIPS/sip-29.md b/SIPS/sip-29.md index 47205e6..b5de432 100644 --- a/SIPS/sip-29.md +++ b/SIPS/sip-29.md @@ -190,8 +190,8 @@ type FungibleAssetUnit = { // Human-friendly name of the asset unit. name: string; - // Ticker of the asset unit. - ticker: string; + // Ticker symbol of the asset unit. + symbol: string; // Number of decimals of the asset unit. decimals: number; @@ -202,8 +202,8 @@ type FungibleAssetMetadata = { // Human-friendly name of the asset. name: string; - // Ticker of the asset. - ticker: string; + // Ticker symbol of the asset's main unit. + symbol: string; // Whether the asset is native to the chain. native: boolean;