Jorge-Lopes / agoric-sdk

monorepo for the Agoric Javascript smart contract platform
Apache License 2.0
0 stars 0 forks source link

Orchestration API: add support for NFTs #33

Open Jorge-Lopes opened 3 months ago

Jorge-Lopes commented 3 months ago

Objective

Extend the Agoric-sdk to support the NFT cosmos module while taking advantage of the Agoric Orchestration package.

Tasks

Available Documentation

Relevant Agoric issues:

Relevant Agoric PR:

Relevant Agoric tests:

Agoric implementation of cosmos.nft.v1beta1

Similar to the protobuf spec of the cosmos.bank.v1beta1 package from the x/bank module, the current version of the agoric-sdk also the has the protobuf spec of the cosmos.nft.v1beta1 package from the x/nft module

agoric-sdk/packages/cosmic-proto/proto/cosmos/nft/v1beta1

Although, for the x/nft module there is NO implementation found on the application level, as we can confirm by the lack of this module on the codegen/cosmos/ directory

agoric-sdk/packages/cosmic-proto/src/codegen/cosmos

Structure of ERTP representation

When we query an NFT class, the response expected will be an QueryClassResponse , with the following structure:

export interface ClassSDKType {
  id: string;
  name: string;
  symbol: string;
  description: string;
  uri: string;
  uri_hash: string;
  data?: AnySDKType;
}

When we query an NFT, the response expected will be an QueryNFTResponse , with the following structure:

export interface NFT {
  classId: string;
  id: string;
  uri: string;
  uriHash: string;
  data?: Any;
}

We should be able to convert the structure above into an ERTP asset, that can be interacted with via the ERTP API. This conversion should be done via a method similar to asAmount .

Each NFT will be described via an Amount , which has a Brand and Value .

Based on the QueryNFTResponse structure, it seems a possible approach would be to define:

Handle remote NFT class and map with issuerKit

Usually, a Brand as a relationship of 1 - 1 with Issuer and Mint . Considering that the Assets we will interact with are created via a contract held on a host chain , it seem to me that it is not expected to have the Mint representation on the controller chain .

Although, for the Issuer the same may not be true, since the issuer has a relevant purpose, we can then rely on them as the authority to validate an Payment of that Brand

Question:How could we get a representation of an Issuer for this specific Brand?

One possible approach is based on the pattern implemented on the pegasus package, more specifically the pegRemote method.

The diagram bellow represent an high level view of how an issuer could be associated with a NFT class issued on the host chain.

sequenceDiagram
    participant c as contract
    participant ov as orchestration vat
    participant ic as icq controller
    participant ih as icq host

    c ->> ov: makeOrchestrationKit
    ov -->> c: orchestrationKit
    c ->> ov: getClass(classId)
    ov ->> ic: query(QueryClassesRequest)
    ic ->> ih: query packet
    ih -->> ic: ack
    ic -->> ov: QueryClassesResponse
    ov -->> c: class
    c ->> ov: registerClass(class)
    ov ->> ov: makeIssuerKit
    ov ->> ov: map(classId, IssuerKit)
    c ->> ov: getIssuerKit(classID)
    ov -->> c: {band, issuer}

Question: How should we record the Class data? Two possible solutions that crossed my mind are:

  1. included in the NFT value itself.
  2. pass the data to the DisplayInfo parameter when calling makeIssuerKit

Question: how to handle ERTP Data Types such as AssetKind? Based on the NFT class data, how can we infer if it should be declared as COPY_SET or COPY_BAG?

Plan exo kit for features related to the x/nft module

Based on the approach described above, the first step before interacting with a remote NFT is to create and register a local representation of NFT Class in the shape of an ERTP asset. That Class should then be mapped with an Issuer and Brand in a durable storage.

Additionally, an NftKit should be created and imported by the OrchestrationKit, which would expose the required methods to:

classDiagram
    LCAKit --* LocalchainAccount
    ICQConnectionKit --* Port
    ICQConnectionKit --* Connection
    ChainAccountKit --* Port
    ChainAccountKit --* Connection
    StakingAccountKit --* IcaAccount

    class ChainAccountKit {
      port: Port
      connection: Connection
      localAddress: LocalIbcAddress
      requestedRemoteAddress: string
      remoteAddress: RemoteIbcAddress
      chainAddress: ChainAddress
    }
    class ICQConnectionKit {
      port: Port
      connection: Connection
      localAddress: LocalIbcAddress
      remoteAddress: RemoteIbcAddress
    }
    class StakingAccountKit {
      chainAddress: ChainAddress 
      bondDenom: string 
      account: ICAAccount 
      timer: Timer
      topicKit: TopicKit
      makeTransferInvitation()
    }

    class LCAKit {
        account: LocalChainAccount
        address: ChainAddress
        topicKit: RecorderKit<LocalChainAccountNotification>
    }
    class LocalchainAccount {
        executeTx()
        deposit()
        withdraw()
    }
    class IcaAccount {
        executeTx()
        deposit()
        getPurse()
        close()
    }
    class NftKit {
        chainAddress: ChainAddress 
        account: ICAAccount 
        icqConnection: ICQConnection
        nftDenom?: string
        getBalance()
        getBalances()
        send()
    }

How to make a local representation of the remote NFT Class on Agoric chain

Two different approaches are being discussed:

The representation bellow follows the same approach as the vat-bank handles new assets. One remaining question is in what Vat should the NftManager live? Would it make sense in the long term to include it in the Orchestration-Vat?

sequenceDiagram
    participant C as Contract
    participant NM as NftManager
    participant NameAdmin
    participant Z as Zone

    NM ->> Z: mapStore('brandToAssetRecord')
    create participant bar as brandToAssetRecord
    Z ->> bar: 
    bar -->> NM: brandToAssetRecord

    NM ->> Z: mapStore('brandToAssetDescriptor')
    create participant bad as brandToAssetDescriptor
    Z ->> bad: 
    bad -->> NM: brandToAssetDescriptor

    C ->> NM: addAsset(nftDenom, issuerName, proposedName)
    NM ->> NM: zcfMint = zcf.makeZCFMint(issuerName, AssetKind.COPY_BAG)
    NM ->> NM: {brand, issuer} = zcfMint.getIssuerRecord()

    NM ->> bar: init(brand, PrivateAssetRecord)
    NM ->> bad: init(brand, ToPublish)
    NM ->> NameAdmin: update(denom, AssetInfo)
Jorge-Lopes commented 3 months ago

Open Questions

anilhelvaci commented 3 months ago

Based on the approach described above, the first step before interacting with a remote NFT is to create and register a local record in the shape of an ERTP asset. With that in mind, an NFT Vat or Contract (ToDo: establish which is the best option) will be created in order to make a remote peg of an NFT Class and expose its Issuer and Brand.

Maybe we can take a look at how vbank handles this. And even extend vbank to be the source of truth for x/nft representations, if makes sense.

classDiagram

    nft --> nft-kit
    orchestration --> nft-kit

    class nft
      nft: -classesPeg Map
      nft: +pegRemote(nftClass)
      nft: +getIssuerKit(nftClassId) {brand, issuer}

    class orchestration
        orchestration: + provideICQConnection() icqConnection
        orchestration: + makeAccount() account

    class nft-kit
        nft-kit: +getBalance(ownerAddr) int
        nft-kit: +getNFTs(brand, ownerAddr) NFT []
        nft-kit: +getNFT(brand, id) NFT
        nft-kit: +getClass(brand) Class
        nft-kit: +send(ica, receiverAddr, brand, id)

I thought we were going to extend orchestrationKit to have NFT related methods.

anilhelvaci commented 3 months ago

Is there a specific reason we're not working on https://github.com/agoric-labs/orchestration-api-spec ? Are we sure the @agoric/orchestration has the correct type spec?

Jorge-Lopes commented 3 months ago

I thought we were going to extend orchestrationKit to have NFT related methods.

Yes, that's correct. The diagram was not updated accordingly (will do it today). Although the api specs and the exo-kit in development follows the design agreed upon

Is there a specific reason we're not working on https://github.com/agoric-labs/orchestration-api-spec ? Are we sure the @agoric/orchestration has the correct type spec?

As you can see from that repo, it was an early drafting environment for the API specification that was not updated in 2 months. For that reason I choose to use a latest version of the agoric-sdk 35d20ebc6d57520d19faea3f6e507e6f9bf1682e to generate the api specs

Jorge-Lopes commented 3 months ago

To build the updated Orchestration API spec, checkout to the branch jorge/orchestration-x/nft.

Then run:

yarn install && yarn build
cd packages/orchestration
yarn typedoc
open docs/index.html

For more detail, see updates implemented on commit f7470aa327d059f9402db0508982b8cd7b316180.

Note: required to update packages/cosmic-proto/dist/helpers.d.ts file toRequestQueryJson: (msg: QueryBalanceRequestProtoMsg | QueryNFTsRequestProtoMsg, ...