ICNS has a decoupled architecture, the system is separated into 4 principal components, and we integrated CAP for history record storage. Main canister IDs are listed below:
Name | Canister ID | CAP Canister |
---|---|---|
registry | e5kvl-zyaaa-aaaan-qabaq-cai | ebop2-oyaaa-aaaan-qabcq-cai |
registrar | e2lt7-uaaaa-aaaan-qabaa-cai | eineg-yqaaa-aaaan-qabda-cai |
default resolver | euj6x-pqaaa-aaaan-qabba-cai | - |
reverse registrar | etiyd-ciaaa-aaaan-qabbq-cai | - |
The ICNS registry stores the list of all domains and subdomains, information about each domain is stored in a Record
, a Record
has the following fields:
public type Record = {
var name : Text; // the ICNS name, e.g. icns.icp
var owner : Principal; // the owner of this name
var resolver : Principal; // the resolver canister id
var ttl : Nat64; // caching time-to-live
var expiry : Time.Time; // name expiry
var controller: Principal; // controller, able to set records for this domain
var operator : Principal; // operator, able to transfer the name
};
Main methods:
1.1 set record for a name, only domain owner can do this
// if success, return CAP tx record id, else return error msg
public type TxReceipt = Result.Result<Nat, Text>;
public shared(msg) func setRecord(
name: Text,
owner: Principal,
resolver: Principal,
ttl: Nat64,
expiry: Time.Time
) : async TxReceipt
1.2 set subdomain record, only the owner/controller of node
name can do this
public shared(msg) func setSubnodeRecord(
node: Text,
sublabel: Text,
owner: Principal,
resolver: Principal,
ttl: Nat64,
expiry: Time.Time
) : async TxReceipt
Note: to prevent attacks, only certain names can add subdomain: registrar
canister can set subdomain of "icp", and reverse registrar
canister can set subdomain of "principal.reverse" for reverse resolution. This limitation will be removed in the future.
1.3 queries
// get record of a name, returns null if name not exist
public query func getRecord(name: Text) : async ?Record
// get names of given user
public query func getUserDomains(key: Principal) : async ?[Record]
// get names the given user controls
public query func getControllerDomains(key: Principal) : async ?[Record]
1.4 the registry also implemented ERC721 style interfaces
public query func balanceOf(user: Principal): async Nat
public query func isApproved(name: Text, who: Principal) : async Bool
public query func getApproved(name: Text): async ?Principal
public query func isApprovedForAll(owner: Principal, op: Principal) : async Bool
public shared(msg) func setApprovalForAll(op: Principal, approved: Bool) : async TxReceipt
public shared(msg) func approve(name: Text, op: Principal): async TxReceipt
public shared(msg) func transfer(to: Principal, name: Text): async TxReceipt
public shared(msg) func transferFrom(from: Principal, to: Principal, name: Text): async TxReceipt
We only support “.icp” top-level domains now, the default registrar is in charge of the “.icp” domain registration. Currently, users can only get ICNS names through auction.
The payment is made with WICP, if a user is outbid in an auction, the WICP payment is credited to the user’s account and can be withdrawn anytime, or use for payment in later auctions.
public type Auction = {
name: Text; // the name in auction
var endTime: Int; // auction end time
var currentBidder: Principal; // current top bidder
var currentBid: Bid; // current top bid
};
public type BidState = {
#lost; // outbid by others
#processing; // temporarily the top bidder
#win; // won the auction
};
public type Bid = {
name: Text; // name for bid
price: Nat; // bid price
timestamp: Time.Time; // bid time
var state: BidState; // bid state
};