Dharma code schoolを試してみる

仮想通貨全般

前回の記事からの続きで以下を試してみました。

チュートリアルでは簡単なローンのスマートコントラクトを作成していきます。

以下のような画面で答えを入力しながら、チュートリアルを進めていきます。

Sponsored Link


チュートリアルを試してみる

チュートリアル1

チュートリアル1で完成したコードは以下です。


pragma solidity 0.4.18;

import "dharma/contracts/BasicTermsContract.sol";

contract SimpleLoanTermsContract is BasicTermsContract {
mapping (bytes32 => uint) etherRepaid;

function getValueRepaid(bytes32 debtAgreementId) returns (uint) {
return etherRepaid[debtAgreementId];
}

function registerRepayment(bytes32 debtAgreementId, uint amount, address token) {
require(isValidRepaymentMessage());
etherRepaid[debtAgreementId] = etherRepaid[debtAgreementId] + amount;
}

function getExpectedRepaymentValue(bytes32 debtAgreementId, bytes32 termsContractParameters, uint blockNumber)
returns (uint) {
uint principal = 1 ether;
uint interestRatePercent = uint(termsContractParameters);
uint interest = principal * interestRatePercent / 100;

if (sevenDaysAfter(getDebtAgreementStartBlockNumber(debtAgreementId)) <= blockNumber) { return 0; } else { return principal + interest; } } function sevenDaysAfter(uint blockNumber) returns (uint) { return blockNumber + 43200; } }

BasicTermsContractを継承したcontractを作成し、以下のようなfunctionを作成しています。

- getValueRepaid : debtAgreementIdの現在の支払う総額
- registerRepayment : 支払う額を登録
- getExpectedRepaymentValue : 支払う利子を求める
- sevenDaysAfter : 7日後のblock番号を返す

チュートリアル2

その後のチュートリアル2ではいよいよ、クリプトキティーを扱います。
以下3つのコントラクトを使います。

- DebtRegistry.sol :
Dharmaプロトコルで債務が発行されると、Dharma債務登録簿には、その返済条件、その債権者の住所、およびその引受債務不履行リスクが保持されます。
- ERC721.sol :
non-fungible token interface。クリプトキティーとやりとりするためのコントラクト
- TermsContract.sol :
チュートリアル1で作成したようなtermsコントラクトとやり取りするためのインターフェース

以下が完成されたコード。

pragma solidity 0.4.18;

import "dharma/contracts/DebtRegistry.sol";
import "dharma/contracts/ERC721.sol";
import "dharma/contracts/TermsContract.sol";

contract KittyCollateralizer {
DebtRegistry debtRegistry;
ERC721 kittyContract;

struct CollateralAgreement {
bytes32 debtAgreementId;
address owner;
uint lockupPeriodEnd;
}

mapping (uint => CollateralAgreement) kittyToCollateralAgreement;

function KittyCollateralizer(
address debtRegistryAddress,
address cryptoKittiesContractAddress
) public
{
debtRegistry = DebtRegistry(debtRegistryAddress);
kittyContract = ERC721(cryptoKittiesContractAddress);
}

function collateralize(bytes32 debtAgreementId, uint kittyId, uint lockupPeriodEnd) {
require(kittyToCollateralAgreement[kittyId].owner == address(0));
require(lockupPeriodEnd > block.number);

kittyContract.transferFrom(msg.sender, this, kittyId);

kittyToCollateralAgreement[kittyId] = CollateralAgreement({
debtAgreementId: debtAgreementId,
owner: msg.sender,
lockupPeriodEnd: lockupPeriodEnd
});
}

function withdrawCollateral(uint kittyId) {
CollateralAgreement collateralAgreement = kittyToCollateralAgreement[kittyId];

require(collateralAgreement.debtAgreementId != bytes32(0));

address creditor = debtRegistry.getCreditor(collateralAgreement.debtAgreementId);
var (termsContractAddress, termsContractParameters) =
debtRegistry.getTerms(collateralAgreement.debtAgreementId);

TermsContract termsContract = TermsContract(termsContract);

uint expectedValueRepaid = termsContract.getExpectedRepaymentValue(agreement.debtAgreementId,
termsContractParameters, block.number);
uint actualValueRepaid = termsContract.getValueRepaid(agreement.debtAgreementId);

if (actualValueRepaid < expectedValueRepaid) { releaseKitty(creditor, kittyId); } else if (block.number > agreement.lockupPeriodEnd) {
releaseKitty(agreement.owner, kittyId);
}

}

function releaseKitty(address to, uint kittyId) internal {
kittyContract.transfer(to, kittyId);
kittyToCollateralAgreement[kittyId] = CollateralAgreement({
debtAgreementId: bytes32(0),
owner: address(0),
lockupPeriodEnd: 0
});
}
}

- CollateralAgreement :
担保の契約情報を保持した構造体
- kittyToCollateralAgreement :
クリプトキティーのIDに紐づいた担保契約情報のmap
- KittyCollateralizer :
コンストラクタ
- collateralize :
担保を実際に実行する。このコントラクトに担保を設定する
kittyContract.transferFrom(msg.sender, this, kittyId);
で、ERC721のtransferFromを使って自分自身に所有権を移転。
- withdrawCollateral :
CryptoKittyの所有者が自分のキティを引き出すことができるブロック番号を現在のブロック番号をすぎていたら
担保設定したownerに所有権が引き継がれる。
関連する債務契約は債務不履行状態になり、その時点で債務契約の債権者はそれを引き出すことができる。
releaseKittyでそれの対応をする。
- releaseKitty :
ERC721の所有権を移転して、該当の担保契約をゼロクリアする

チュートリアル3

債務のオーダーを作成していきます。javascriptでのコードでした。

Dharmaインスタンスを作成します。

import {Dharma} from "dharma.js";
const dharma = new Dharma(web3);

kittyid は 9226。Jake という名前のクリプトキティーだそうです。

const debtorAccount = "0x5f3ed6552194ab5d78f19fa824e302de840ae0c2";
const kittyId = new BigNumber(9226);

contractをdeployします。

const simpleLoanTermsContract = await SimpleLoanTermsContract.deployed();
const kittyCollateralizerContract = await KittyCollateralizer.deployed();
const cryptoKittiesCoreContract = await CryptoKittiesCore.deployed();
const wrappedEtherContract = await WrappedEtherContract.deployed();
const debtTokenContract = await dharma.getDebtTokenContract();

債務の注文をつくります。ブロックチェーンにbroadcastする必要がなく完全にオフラインで作れます。

const debtOrder = await dharma.generateSignedDebtOrder({
debtor: debtorAccount,
principalAmount: web3.eth.toWei(1, 'ether'),
principalToken: wrappedEtherContract.address,
termsContract: simpleLoanTermsContract.address,
termsContractParameters: 20
})

ロックアップする期間を2週間にします。

const lockupPeriodEnd = web3.eth.blockNumber + 86400; // Two week lockup period from present

以前に作ったKittyCollateralizerコントラクトでJakeを担保として差し入れます。
この契約では担保機能のアカウントからJakeを「引き出す」必要があるため
1. 最初にKittyCollateralizer契約を承認して
2. Jakeの所有権を取得する必要があります。

await cryptoKittiesCoreContract.approve(kittyCollateralizerContract.address, kittyId, { from: debtorAccount });
await kittyCollateralizerContract.collateralize(debtOrder.agreementId, kittyId, lockupPeriod);

debtOrder.collateralKittyId = kittyId;

債務注文のデータ情報です。


console.log(debtOrder);

{
agreementId: '0x56d52b380a146dd623e714c88e82c495dd6ad55ef389af895458db7dc50d1825',
version: '0x5dc2c3f821ebdc3e91878c4c97748931cf30cb88',
debtor: '0x5f3ed6552194ab5d78f19fa824e302de840ae0c2',
principleAmount: { [String:'1000000000000000000'] s: 1, e: 18, c: [ 10000 ] },
principleTokenAddress: 0x85fa6c80d1eea4b4c2997abb60a700e78c97e660',
termsContract: '0xe879563679189671f0cf2aec69a385c3eddd8421',
termsContractParameters: { [String: '20'] s: 1, e: 1, c: [ 20 ] },
underwriter: '0x0000000000000000000000000000000000000000',
underwriterRiskRating: { [String: '0'] s: 1, e: 0, c: [ 0 ] },
underwriterFee: { [String: '0'] s: 1, e: 0, c: [ 0 ] },
debtorFee: { [String: '0'] s: 1, e: 0, c: [ 0 ] },
creditorFee: { [String: '0'] s: 1, e: 0, c: [ 0 ] },
zeroExExchangeContract: '0xe9e141e498cf2cbf3da5fe1e813e760a2b77ae1c',
expiration: { [String: '1514852991252'] s: 1, e: 12, c: [ 1514852991252 ] },
salt:
{ [String: '12919442725419558340276999437268704821189453747962794497108598142355137295885']
s: 1,
e: 76,
c:
[ 1291944,
27254195583402,
76999437268704,
82118945374796,
27944971085981,
42355137295885 ] },
debtorSignature:
{ r: '0x8e146a96b7ddc0b067dc8786774d6462bf8a5e42b8d3bb27701740b69721bdff',
s: '0x2e56d9fd848faf10adfe17dd21de235d8ce3ff67d306ee7eaecc550d33c07cdc',
v: 28 },
collateralKittyId: { [String: '9226'] s: 1, e: 3, c: [ 9226 ] }
}

0xプロトコルの取引注文と同様に、Dharmaプロトコルの債務注文は、2つの当事者間で債務契約を締結および開始するために使用できる単なるテキストの文字列です。
債務者が債務注文を生成するとき、彼らは今、このテキストの文字列を受け取り、選択した任意の媒体によって潜在的な債権者にそれをbroadcastすることができます。
潜在的な債権者は、付属の「collateralKittyId」値を使用して、この特定のキティがこの次回の債務契約の担保として保持されていることをKittyCollateralizer契約で確認できます。

実演目的で、債権者の立場に身を置きましょう。 Twitterであなたの友人Ivanと共有している連続した債務注文に遭遇したとします。

あなたは、Ivanの暗号性であるJakeがこの債務契約の担保として保持されていることを確認し、Ivanが返済を怠った場合、Jakeの耐えられない可愛さと否定できない市場価値を考慮してあなたの投資は安全に担保で賄われると決めます。あなたは、債権者として、あなたがイワンの借金命令を満たすことを望んでいると決めました。

債権者が注文を履行したいとき、彼らは連続した債務注文を取り、以下を実行します。

Dharmaの契約は、債務注文が満たされたときに元本の支払いを債権者の口座から「引き出す」ことになるので、債権者は最初にERC20承認方法を使用して元本の支払いを決済する必要があります(dharma JSライブラリーにはヘルパー機能が組み込まれています) )

次に債権者は、連番の債務注文を受け取り、それをDharmaのスマート契約に提出して、債務注文を処理し、債務契約を開始します。

この仕組みは、Dharmaのホワイトペーパーで詳しく説明されています - これらすべては、fillDebtOrder関数にきちんと抽象化されています。


const creditorAccount = 0x0fa35a502d2109b1989790eba56b11fd60c1ca1e';

await dharma.approvePrincipalPaymentAmount(debtOrder.principalAmount, { from: creditorAccount });
await dharma.fillDebtOrder(debtOrder, { from: creditorAccount });

債務者は1etherでクレジットされており、債権者にはローンの将来のキャッシュフローに対する権利を表す固有の債務トークンが発行されていることがわかります。


const debtorBalance = await wrappedEtherContract.balanceOf(debtorAccount);
const debtTokenOwner = await debtTokenContract.ownerOf(debtOrder.agreementId);

console.log(debtorBalance.toString());
// '1000000000000000000'
console.log(debtTokenOwner == creditorAccount);
// true

どのタイミングでも、債務者は以下のことを行って返済することができます。

await dharma.repay(debtOrder.agreementId, web3.toWei(0.5, 'ether'), { from: debtorAccount });

さらに、ローンに対する債権者の出資額は標準のERC721トークンです - 出資額はいつでも任意の口座に振り替えることができます。 返済は、返済時に債務トークンを保持している人(またはどちらかの契約)に振り分けられます。


const newOwner = '0x46642a868d9615766a72445644020b756ef3bb1b';
await dharma.token.transfer(debtOrder.agreementId, newOwner);

1週間が経過しても、債務者が残りの0.7のEtherを返済していない場合、債権者は担保契約からJakeを差し押さえる資格があります。 彼は私たちが以前に構築したwithdrawCollateral関数を呼び出すことによってこれを行います。


await kittyCollateralizerContract.withdrawCollateral(kittyId, { from: creditorAccount });
const kittyOwner = cryptoKittiesCoreContract.ownerOf(kittyId);
console.log(kittyOwner == creditorAccount);
// true

そして、債権者は現在Jakeの監護権を持っています。 債権者は、元本を回収するためにJakeを売却することも、Jakeを維持することもできます。

ここまでが、チュートリアルの説明でした。

まとめ

簡単に担保付きのコントラクトがこれでできたことになります。
パブリックなブロックチェーンに担保のプログラムがのる事によって、不正しようがない担保ができるというのは本当に面白いです。
本当に契約を全てプログラム化できる土壌が整ってきているから、そのうち簡単な融資審査やローン設計とかこれで全部できちゃいそうですね。

Sponsored Link
コメントはまだありません

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


仮想通貨全般
Dharma CDO発行プラットフォーム?

DharmaがCDO発行プラットフォームなる事をhoryさんが以下ツイートでつぶやいていたので、気に …

仮想通貨全般
ZCash、Ethereumでも使われているゼロ知識証明とは?

最近話題沸騰の「ゼロ知識証明」、またの名を「Zk-Snark」を簡単にですが調べてみました。 参考ペ …

Dapps
Plasma Porker をとりあえず動かす

最近、Plasma界隈で賑わしている Plasma Prime。 Plasma Prime desi …