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

SIP-29: Snap Assets API #154

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
232 changes: 232 additions & 0 deletions SIPS/sip-29.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
---
sip: 29
title: Snap Assets API
status: Draft
author: Daniel Rocha (@danroc), Guillaume Roux (@GuillaumeRx)
created: 2024-12-05
---

## Abstract

This SIP aims to define a new API that can be exposed by Snaps to allow clients
to retrieve asset information in a chain-agnostic way.

## Motivation

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

> 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

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 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.
danroc marked this conversation as resolved.
Show resolved Hide resolved

### Snap Implementation

Two methods are defined in the Snap Assets API:

Any Snap that wishes to provide asset information **MUST** implement the following API:
danroc marked this conversation as resolved.
Show resolved Hide resolved

#### Get Asset Metadata

```typescript
import { OnAssetLookupHandler } from "@metamask/snaps-sdk";

export const onAssetLookup: OnAssetLookupHandler = async ({
assets
}) => {
const assetsMetadata = /* Get metadata */;
danroc marked this conversation as resolved.
Show resolved Hide resolved
return { assets: assetsMetadata };
};
```

The type for an `onAssetLookup` handler function’s arguments is:

```typescript
interface OnAssetLookupArgs {
assets: Caip19AssetType[];
}
```

The type for an `onAssetLookup` handler function’s return value is:

```typescript
type OnAssetLookupReturn = {
danroc marked this conversation as resolved.
Show resolved Hide resolved
assets: Record<Caip19AssetType, AssetMetadata>;
danroc marked this conversation as resolved.
Show resolved Hide resolved
};
```

#### Get Asset Conversion Rate

```typescript
import { OnAssetConversionHandler } from "@metamask/snaps-sdk";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this function implemented within the @metamask/snap-sdk ? is it part of the assets team scope ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the Assets team will be a caller of this method, but its implementation will be handled by other teams (Solana and Snaps).


export const onAssetConversion: OnAssetConversionHandler = async ({
conversions
}) => {
const conversionRates = /* Get conversion rate */;
danroc marked this conversation as resolved.
Show resolved Hide resolved
return { conversionRates };
};
```

The type for an `onAssetConversion` handler function’s arguments is:

```typescript
type Conversion = {
from: Caip19AssetType;
to: Caip19AssetType;
};

type OnAssetConversionArgs = {
conversions: Conversion[];
};
```

The type for an `onAssetConversion` handler function’s return value is:

```typescript
type AssetConversionRate = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we get historical market data?

// 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 will expire.
expirationTime: number;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @danroc
The data we currently have does not include MarketData, which means we cannot display the 1-day price changes or chart-based price changes. I believe this should be updated to include an interface like the following:

interface MarketData {
	id: string;
	price: number;
	marketCap: number;
	allTimeHigh: number;
	allTimeLow: number;
	totalVolume: number;
	high1d: number;
	circulatingSupply: number;
	dilutedmarketCap: number;
	marketCapPercentChange1d: number;
	priceChange1d: number;
	pricePercentChange1h: number;
	pricePercentChange1d: number;
	pricePercentChange7d: number;
	pricePercentChange14d: number;
	pricePercentChange30d: number;
	pricePercentChange200d: number;
	pricePercentChange1y: number;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @salimtb, thanks! We didn’t plan to include market data in the first release, so we left it out of this API to give ourselves more time to figure out what’s really needed.

Are there any fields you’d like to add or remove from the current EVM implementation? Now might be a good time to make any changes you’ve been thinking about.

Also, it’s not a big deal if it gets added later to this SIP or even a new one.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for your response, @danroc. Not all the fields are currently in use, but we decided to keep them for potential future features. If you'd like, I can provide you with a list of the properties that are currently being used.
all of the properties are returned by the endpoint /spot-price of the price API

};

type FromAsset = Conversion["from"];

type ToAsset = Conversion["to"];

type OnAssetConversionReturn = {
danroc marked this conversation as resolved.
Show resolved Hide resolved
conversionRates: Record<From, Record<To, AssetConversionRate>>;
};
```

### Fiat currency representation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should create a CAIP for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point. This is technically CAIP-19, but with unregistered namespaces.

I started a discussion on the CAIPs repo, let's see what other think of it.


We SHOULD use CAIP-19 to represent fiat currencies as well. This approach
danroc marked this conversation as resolved.
Show resolved Hide resolved
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
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 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.

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
```

## 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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we identify other types of tokens that need to be handled differently?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some examples might be differentiating between brc-20 and rune tokens on bitcoin. Or eth, erc20s, and staking contracts on ethereum. I anticipate more types of tokens than just native vs not. Not sure if anything built into caip19 already helps distinguish this.


// Represents a fungible asset
fungible: true;

// Base64 representation of the asset icon.
iconBase64: string;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be an URL link to the icon?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it has to be an icon, the static token icon server is going to return pngs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Do we want to limit the size of this icon somehow?


// List of asset units.
units: FungibleAssetUnit[];
};
```

## 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
Loading