visit
dfx: sh -ci "$(curl -fsSL //smartcontracts.org/install.sh)"
Rust: install via the command "curl --proto '=https' --tlsv1.2 -sSf //sh.rustup.rs | sh"
.
NodeJS: installed with the command "apt install nodejs; apt install npm; npm install -g n; n lts"
.
CMake: install with the command "apt install cmake"
.
$git clone //github.com/dfinity/internet-identity.git
$cd internet-identity
$npm install
$dfx start --clean --background
$II_ENV=development dfx deploy --no-wallet --argument '(null)'
$dfx canister id internet_identity
Note: You need to write down the II_Canister_ID of the II canister obtained by "dfx canister id internet_identity"
.
import { AuthClient } from "@dfinity/auth-client";
let identityProvider = '//lm5fh-ayaaa-aaaah-aafua-cai.localhost:8000';
let identity;
try {
const authClient = await AuthClient.create();
if (await authClient.isAuthenticated()) {
identity = authClient.getIdentity();
} else {
identity = await new Promise((resolve, reject) => {
let timer = setTimeout(() => {
timer = null;
reject('do II auth timeout!');
}, 30 * 1000);
authClient.login({
identityProvider,
maxTimeToLive: BigInt(60_000_000_000),
onSuccess: () => {
if (timer != null) {
clearTimeout(timer);
timer = null;
resolve(authClient.getIdentity());
}
},
onError: (err) => {
if (timer != null) {
clearTimeout(timer);
timer = null;
reject(err);
}
},
});
});
}
} catch (e) {
console.log(e);
}
console.log(identity);
identityProvider
specifies the url path to the II authentication service. If not specified, then the default is identity.ic0.app for the main network. localII path is provided here.
AuthClient.create()
creates the auth client and will restore the identity from local storage if the Internet Identity has been done and has not expired.
authClient.getIdentity()
is used to retrieve the identity, which may be II-authenticated or anonymously generated.
authClient.isAuthenticated()
is used to check if the current identity is II-authenticated.
identityProvider
Provides the url path to the authentication service, the default is identity.ic0.app.
maxTimeToLive
provides the valid duration of the delegate proxy identity in ns.
const nextExpiration = identity.getDelegation().delegations
.map(d => d.delegation.expiration)
.reduce((current, next) => next < current ? next : current);
const expirationDuration = nextExpiration - BigInt(Date.now()) * BigInt(1000_000);
import {Actor, HttpAgent} from "@dfinity/agent";
const agent = new HttpAgent({identity}); //identity is an identity authenticated by II, if it is null, it defaults to an anonymous identity.
await agent.fetchRootKey();
const idlFactory = ({ IDL }) =>
IDL.Service({
whoami: IDL.Func([], [IDL.Principal], ['query']),
});
const canisterId = "lm5fh-ayaaa-aaaah-aafua-cai";
const actor = Actor.createActor(idlFactory, {agent, canisterId});
const principal = await actor.whoami();
new HttpAgent({identity})
Generate agent for proxy requests. The agent uses the specified identity as the identity subject of the request, or an anonymous identity if not specified.
agent.fetchRootKey()
is used to pull the rootkey, because the built-in rootkey is for the main network environment. The code can only be used in the development environment, and must not be used in the main network environment.
idlFactory
defines the interface of canister. canisterId defines the ID of canister.
Actor.createActor
will create the actor.
import { AuthClient } from "@dfinity/auth-client";
let identityProvider = null;
let identity;
try {
const authClient = await AuthClient.create();
if (await authClient.isAuthenticated()) {
identity = authClient.getIdentity();
} else {
identity = await new Promise((resolve, reject) => {
let timer = setTimeout(() => {
timer = null;
reject('do II auth timeout!');
}, 30 * 1000);
authClient.login({
identityProvider,
maxTimeToLive: BigInt(60_000_000_000),
onSuccess: () => {
if (timer != null) {
clearTimeout(timer);
timer = null;
resolve(authClient.getIdentity());
}
},
onError: (err) => {
if (timer != null) {
clearTimeout(timer);
timer = null;
reject(err);
}
},
});
});
}
} catch (e) {
console.log(e);
}
console.log(identity);
identityProvider specifies the url path to the II authentication service. If not specified, then the default is identity.ic0.app for the main network.
const nextExpiration = identity.getDelegation().delegations
.map(d => d.delegation.expiration)
.reduce((current, next) => next < current ? next : current);
const expirationDuration = nextExpiration - BigInt(Date.now()) * BigInt(1000_000);
import {Actor, HttpAgent} from "@dfinity/agent";
const agent = new HttpAgent({identity}); // identity is an identity authenticated by II, if it is null, it defaults to an anonymous identity.
// await agent.fetchRootKey(); // The main network environment cannot pull the rootkey, otherwise it may lead to man-in-the-middle attack.
const idlFactory = ({ IDL }) =>
IDL.Service({
whoami: IDL.Func([], [IDL.Principal], ['query']),
});
const canisterId = "lm5fh-ayaaa-aaaah-aafua-cai";
const actor = Actor.createActor(idlFactory, {agent, canisterId});
const principal = await actor.whoami();
new HttpAgent({identity})
Generates an agent for proxy requests that uses the specified identity as the identity subject of the request, or an anonymous identity if it is not specified.
{
"canisters": {
"Nomos": {
"main": "src/Nomos/main.mo",
"type": "motoko"
},
"Nomos_assets": {
"dependencies": [
"Nomos"
],
"frontend": {
"entrypoint": "src/Nomos_assets/src/index.html"
},
"source": [
"src/Nomos_assets/assets",
"dist/Nomos_assets/"
],
"type": "assets"
}
},
...
}
Then import the IDL definition of canister as well as the canister ID. e.g. import {idlFactory as customNomosIDL, canisterId as customNomosID} from "dfx-generated/Nomos";
where Nomos is the name of the canister.
const nomosActor = Actor.createActor(customNomosIDL, {agent, canisterId: customNomosID});
Once the actor is successfully created you can send a request like canister. For example.
let userInfo = await nomosActor.userInfo()
const canisterId = Principal.fromText(canisterIdEl.value);
const idlFactory = ({ IDL }) =>
IDL.Service({
whoami: IDL.Func([], [IDL.Principal], ['query']),
});
An Actor can be created when the IDL definition of canister and canister ID are available. e.g.
const whoamiActor = Actor.createActor(idlFactory, {agent, canisterId});
Where agent is the request agent, see Identity Agent Request for details.
Once the actor is successfully created you can send a request like canister. For example.
let principal = await actor.whoami()
({ IDL }) =>
IDL.Service({
whoami: IDL.Func([], [IDL.Principal], ['query']),
register : IDL.Func([IDL.Text, IDL.Text],[IDL.Bool, IDL.Bool, IDL.Nat],[],),
});
type HttpResponse =
record {
body: blob;
headers: vec HeaderField;
status_code: nat16;
};
type HttpRequest =
record {
body: blob;
headers: vec HeaderField;
method: text;
url: text;
};
type HeaderField =
record {
text;
text;
};
service : {
“http_request”: (HttpRequest) -> (HttpResponse) query;
}
import Text "mo:base/Text";
actor {
type HeaderField = (Text, Text);
type HttpRequest = {
method: Text;
url: Text;
headers: [HeaderField];
body: Blob;
};
type HttpResponse = {
status_code: Nat16;
headers: [HeaderField];
body: Blob;
};
public query func http_request(req: HttpRequest) : async HttpResponse {
return {
status_code=200;
headers= [("content-type","text/plain")];
body=Text.encodeUtf8("hello boy!");
};
};
};
use ic_cdk::export::{candid::{CandidType, Deserialize}};
use ic_cdk_macros::*;
use serde_bytes::{ByteBuf};
type HeaderField = (String, String);
#[derive(Clone, Debug, CandidType, Deserialize)]
struct HttpRequest {
method: String,
url: String,
headers: Vec<(String, String)>,
body: ByteBuf,
}
#[derive(Clone, Debug, CandidType, Deserialize)]
struct HttpResponse {
status_code: u16,
headers: Vec<HeaderField>,
body: Vec<u8>,
}
#[query]
async fn http_request(_req: HttpRequest) -> HttpResponse {
let mut headers: Vec<HeaderField> = Vec::new();
headers.push(("content-type".to_string(), "text/plain".to_string()));
return HttpResponse {
status_code: 200,
headers,
body: "hello boy!".as_bytes().to_vec(),
}
}
type canister_id = principal;
type user_id = principal;
type wasm_module = blob;
type canister_settings = record {
controllers : opt vec principal;
compute_allocation : opt nat;
memory_allocation : opt nat;
freezing_threshold : opt nat;
};
type definite_canister_settings = record {
controllers : vec principal;
compute_allocation : nat;
memory_allocation : nat;
freezing_threshold : nat;
};
service ic : {
create_canister : (record {
settings : opt canister_settings
}) -> (record {canister_id : canister_id});
update_settings : (record {
canister_id : principal;
settings : canister_settings
}) -> ();
install_code : (record {
mode : variant {install; reinstall; upgrade};
canister_id : canister_id;
wasm_module : wasm_module;
arg : blob;
}) -> ();
uninstall_code : (record {canister_id : canister_id}) -> ();
start_canister : (record {canister_id : canister_id}) -> ();
stop_canister : (record {canister_id : canister_id}) -> ();
canister_status : (record {canister_id : canister_id}) -> (record {
status : variant { running; stopping; stopped };
settings: definite_canister_settings;
module_hash: opt blob;
memory_size: nat;
cycles: nat;
});
delete_canister : (record {canister_id : canister_id}) -> ();
deposit_cycles : (record {canister_id : canister_id}) -> ();
raw_rand : () -> (blob);
// provisional interfaces for the pre-ledger world
provisional_create_canister_with_cycles : (record {
amount: opt nat;
settings : opt canister_settings
}) -> (record {canister_id : canister_id});
provisional_top_up_canister :
(record { canister_id: canister_id; amount: nat }) -> ();
}
create_canister : (record {settings : opt canister_settings}) -> (record {canister_id : canister_id});
compute_allocation (nat)
: must be a number between 0 and 100, including 0 and 100, with a default value of 0. It indicates how much compute power should be guaranteed for this container, representing the percentage of the maximum compute power that can be allocated for a single container. If the system is unable to provide the requested allocation, for example because it is overbooked, the call will be rejected.
memory_allocation (nat)
: must be a number between 0 and 2^48 (i.e. 256TB), inclusive, with a default value of 0. It indicates how much memory the container is allowed to use in total. Any attempt to increase memory usage beyond this allocation will fail. If the system is unable to provide the requested allocation, for example because it is overbooked, the call will be rejected. If set to 0, the container's memory will grow as best it can and be limited by the memory available on the network.
freezing_threshold (nat)
: must be a number between 0 and 2^64-1 inclusive, and indicates the length of time (in seconds), default value: 2592000 (approximately 30 days). Considering the current size of the container and the current storage cost of the system, the container is considered frozen when the system estimates that the container will run out of cycles after freeze_threshold seconds.
update_settings : (record {canister_id : principal; settings : canister_settings}) -> ();
canister_id
specifies the id of the canister whose settings need to be updated.
settings
is the same as settings in create_canister, if a field is not included in settings, it means that the field is not changed.
install_code : (record {mode : variant {install; reinstall; upgrade}; canister_id : canister_id; wasm_module : wasm_module; arg : blob;}) -> ();
uninstall_code : (record {canister_id : canister_id}) -> ();
canister
is now empty. In particular, any incoming or queued calls will be rejected.
canister_status : (record {canister_id : canister_id}) -> (record {
status : variant { running; stopping; stopped }; settings: definite_canister_settings;
module_hash: opt blob; memory_size: nat; cycles: nat;});
Indicates various information about the canister. Only the controller of the container can request its status. It contains status.
stop_canister : (record {canister_id : canister_id}) -> ();
start_canister : (record {canister_id : canister_id}) -> ();
delete_canister : (record {canister_id : canister_id}) -> ();
deposit_cycles : (record {canister_id : canister_id}) -> ();
raw_rand
raw_rand : () -> (blob);
This method accepts no input and returns 32 pseudo-random bytes to the caller. The return value is not known to any part of the IC at the time this call is submitted. A new return value is generated each time this method is called.
actor Counterer {
type CounterIn = actor {
get : shared query () -> async Nat;
set : (n: Nat) -> async ();
inc : () -> async ();
};
var counter : ?CounterIn = null;
// set counter canister ID.
public func init(c : Text) {
counter := ?actor(c);
};
// Get the value of the counter.
public func get() : async Nat {
switch counter {
case (?c) { await c.get();};
case null {0};
};
};
// Set the value of the counter.
public func set(n: Nat) {
switch counter {
case (?c) {await c.set(n);};
case null {()};
};
};
// Increment the value of the counter.
public func inc() {
switch counter {
case (?c) {await c.inc();};
case null {()};
};
};
};
type TransactionNotification =
record {
amount: ICPTs;
block_height: BlockHeight;
from: principal;
from_subaccount: opt SubAccount;
memo: Memo;
to: principal;
to_subaccount: opt SubAccount;
};
type SubAccount = vec nat8;
type Result =
variant {
Err: text;
Ok;
};
type Memo = nat64;
type ICPTs = record {e8s: nat64;};
type BlockHeight = nat64;
service : {
transaction_notification: (TransactionNotification) -> (Result);
}
Also published on: