1 Star 0 Fork 0

张东东大人 / delphereum

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
web3.eth.fulcrum.pas 16.59 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
{******************************************************************************}
{ }
{ Delphereum }
{ }
{ Copyright(c) 2020 Stefan van As <svanas@runbox.com> }
{ Github Repository <https://github.com/svanas/delphereum> }
{ }
{ Distributed under Creative Commons NonCommercial (aka CC BY-NC) license. }
{ }
{ need tokens to test with? }
{ 1. make sure your wallet is set to the relevant testnet }
{ 2. go to https://faucet.kovan.network }
{ 3. get yourself some KETH (aka Kovan Ether) }
{ 4. go to https://oasis.app/?network=kovan, click on Borrow }
{ 5. using your KETH as collateral, generate yourself some DAI }
{ }
{******************************************************************************}
unit web3.eth.fulcrum;
{$I web3.inc}
interface
uses
// Velthuis' BigNumbers
Velthuis.BigIntegers,
// web3
web3,
web3.eth,
web3.eth.defi,
web3.eth.erc20,
web3.eth.logs,
web3.eth.types,
web3.utils;
type
EFulcrum = class(EWeb3);
TFulcrum = class(TLendingProtocol)
protected
class procedure Approve(
client : TWeb3;
from : TPrivateKey;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncReceipt);
class procedure TokenToUnderlying(
client : TWeb3;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncQuantity);
class procedure UnderlyingToToken(
client : TWeb3;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncQuantity);
public
class function Name: string; override;
class function Supports(
chain : TChain;
reserve: TReserve): Boolean; override;
class procedure APY(
client : TWeb3;
reserve : TReserve;
_period : TPeriod;
callback: TAsyncFloat); override;
class procedure Deposit(
client : TWeb3;
from : TPrivateKey;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncReceipt); override;
class procedure Balance(
client : TWeb3;
owner : TAddress;
reserve : TReserve;
callback: TAsyncQuantity); override;
class procedure Withdraw(
client : TWeb3;
from : TPrivateKey;
reserve : TReserve;
callback: TAsyncReceiptEx); override;
class procedure WithdrawEx(
client : TWeb3;
from : TPrivateKey;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncReceiptEx); override;
end;
TOnMint = reference to procedure(
Sender : TObject;
Minter : TAddress;
TokenAmount: BigInteger;
AssetAmount: BigInteger;
Price : BigInteger);
TOnBurn = reference to procedure(
Sender : TObject;
Burner : TAddress;
TokenAmount: BigInteger;
AssetAmount: BigInteger;
Price : BigInteger);
TiToken = class abstract(TERC20)
strict private
FOnMint: TOnMint;
FOnBurn: TOnBurn;
procedure SetOnMint(Value: TOnMint);
procedure SetOnBurn(Value: TOnBurn);
protected
function ListenForLatestBlock: Boolean; override;
procedure OnLatestBlockMined(log: TLog); override;
public
constructor Create(aClient: TWeb3); reintroduce; overload; virtual; abstract;
//------- read from contract -----------------------------------------------
procedure AssetBalanceOf(owner: TAddress; callback: TAsyncQuantity);
procedure LoanTokenAddress(callback: TAsyncAddress);
procedure SupplyInterestRate(callback: TAsyncQuantity);
procedure TokenPrice(callback: TAsyncQuantity);
//------- write to contract ------------------------------------------------
procedure Burn(from: TPrivateKey; amount: BigInteger; callback: TAsyncReceipt);
procedure Mint(from: TPrivateKey; amount: BigInteger; callback: TAsyncReceipt);
//------- events -----------------------------------------------------------
property OnMint: TOnMint read FOnMint write SetOnMint;
property OnBurn: TOnBurn read FOnBurn write SetOnBurn;
end;
TiDAI = class(TiToken)
public
constructor Create(aClient: TWeb3); override;
end;
TiUSDC = class(TiToken)
public
constructor Create(aClient: TWeb3); override;
end;
TiUSDT = class(TiToken)
public
constructor Create(aClient: TWeb3); override;
end;
implementation
uses
// Delphi
System.Math,
System.TypInfo;
type
TiTokenClass = class of TiToken;
const
iTokenClass: array[TReserve] of TiTokenClass = (
TiDAI,
TiUSDC,
TiUSDT
);
{ TFulcrum }
// Approve the iToken contract to move your underlying asset.
class procedure TFulcrum.Approve(
client : TWeb3;
from : TPrivateKey;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncReceipt);
var
erc20 : TERC20;
iToken: TiToken;
begin
iToken := iTokenClass[reserve].Create(client);
if Assigned(iToken) then
begin
iToken.LoanTokenAddress(procedure(addr: TAddress; err: IError)
begin
try
if Assigned(err) then
callback(nil, err)
else
begin
erc20 := TERC20.Create(client, addr);
if Assigned(erc20) then
begin
erc20.ApproveEx(from, iToken.Contract, amount, procedure(rcpt: ITxReceipt; err: IError)
begin
try
callback(rcpt, err);
finally
erc20.Free;
end;
end);
end;
end;
finally
iToken.Free;
end;
end);
end;
end;
class procedure TFulcrum.TokenToUnderlying(
client : TWeb3;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncQuantity);
var
iToken: TiToken;
begin
iToken := iTokenClass[reserve].Create(client);
if Assigned(iToken) then
try
iToken.TokenPrice(procedure(price: BigInteger; err: IError)
begin
if Assigned(err) then
callback(0, err)
else
callback(BigInteger.Create(amount.AsExtended * (price.AsExtended / 1e18)), nil);
end);
finally
iToken.Free;
end;
end;
class procedure TFulcrum.UnderlyingToToken(
client : TWeb3;
reserve : TReserve;
amount : BIgInteger;
callback: TAsyncQuantity);
var
iToken: TiToken;
begin
iToken := iTokenClass[reserve].Create(client);
if Assigned(iToken) then
try
iToken.TokenPrice(procedure(price: BigInteger; err: IError)
begin
if Assigned(err) then
callback(0, err)
else
callback(BigInteger.Create(amount.AsExtended / (price.AsExtended / 1e18)), nil);
end);
finally
iToken.Free;
end;
end;
class function TFulcrum.Name: string;
begin
Result := 'Fulcrum';
end;
class function TFulcrum.Supports(chain: TChain; reserve: TReserve): Boolean;
begin
Result := (chain in [Mainnet, Kovan]) or ((chain = BSC_main_net) and (reserve = USDT));
end;
// Returns the annual yield as a percentage with 4 decimals.
class procedure TFulcrum.APY(
client : TWeb3;
reserve : TReserve;
_period : TPeriod;
callback: TAsyncFloat);
var
iToken: TiToken;
begin
iToken := iTokenClass[reserve].Create(client);
try
iToken.SupplyInterestRate(procedure(value: BigInteger; err: IError)
begin
if Assigned(err) then
callback(0, err)
else
callback(BigInteger.Divide(value, BigInteger.Create(1e14)).AsInt64 / 1e4, nil);
end);
finally
iToken.Free;
end;
end;
// Deposits an underlying asset into the lending pool.
class procedure TFulcrum.Deposit(
client : TWeb3;
from : TPrivateKey;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncReceipt);
var
iToken: TiToken;
begin
// Before supplying an asset, we must first approve the iToken.
Approve(client, from, reserve, amount, procedure(rcpt: ITxReceipt; err: IError)
begin
if Assigned(err) then
callback(nil, err)
else
begin
iToken := iTokenClass[reserve].Create(client);
try
iToken.Mint(from, amount, callback);
finally
iToken.Free;
end;
end;
end);
end;
// Returns how much underlying assets you are entitled to.
class procedure TFulcrum.Balance(
client : TWeb3;
owner : TAddress;
reserve : TReserve;
callback: TAsyncQuantity);
begin
var BalanceOf := procedure(callback: TAsyncQuantity)
begin
var iToken := iTokenClass[reserve].Create(client);
try
iToken.AssetBalanceOf(owner, callback);
finally
iToken.Free;
end;
end;
var Decimals := procedure(callback: TAsyncQuantity)
begin
var iToken := iTokenClass[reserve].Create(client);
try
iToken.Decimals(callback);
finally
iToken.Free;
end;
end;
BalanceOf(procedure(balance: BigInteger; err: IError)
begin
if Assigned(err) then
begin
callback(balance, err);
EXIT;
end;
Decimals(procedure(decimals: BigInteger; err: IError)
begin
if Assigned(err) then
begin
callback(balance, err);
EXIT;
end;
if reserve.Decimals = Power(10, decimals.AsInteger) then
callback(balance, err)
else
callback(reserve.Scale(balance.AsExtended / Power(10, decimals.AsInteger)), err);
end);
end);
end;
// Redeems your balance of iTokens for the underlying asset.
class procedure TFulcrum.Withdraw(
client : TWeb3;
from : TPrivateKey;
reserve : TReserve;
callback: TAsyncReceiptEx);
var
iToken: TiToken;
begin
from.Address(procedure(addr: TAddress; err: IError)
begin
if Assigned(err) then
begin
callback(nil, 0, err);
EXIT;
end;
iToken := iTokenClass[reserve].Create(client);
if Assigned(iToken) then
begin
// step #1: get the iToken balance
iToken.BalanceOf(addr, procedure(amount: BigInteger; err: IError)
begin
try
if Assigned(err) then
begin
callback(nil, 0, err);
EXIT;
end;
// step #2: redeem iToken-amount in exchange for the underlying asset
iToken.Burn(from, amount, procedure(rcpt: ITxReceipt; err: IError)
begin
if Assigned(err) then
begin
callback(nil, 0, err);
EXIT;
end;
TokenToUnderlying(client, reserve, amount, procedure(output: BigInteger; err: IError)
begin
if Assigned(err) then
callback(rcpt, 0, err)
else
callback(rcpt, output, nil);
end);
end);
finally
iToken.Free;
end;
end);
end;
end);
end;
class procedure TFulcrum.WithdrawEx(
client : TWeb3;
from : TPrivateKey;
reserve : TReserve;
amount : BigInteger;
callback: TAsyncReceiptEx);
var
iToken: TiToken;
begin
// step #1: from underlying-amount to iToken-amount
UnderlyingToToken(client, reserve, amount, procedure(input: BigInteger; err: IError)
begin
if Assigned(err) then
begin
callback(nil, 0, err);
EXIT;
end;
iToken := iTokenClass[reserve].Create(client);
if Assigned(iToken) then
try
// step #2: redeem iToken-amount in exchange for the underlying asset
iToken.Burn(from, input, procedure(rcpt: ITxReceipt; err: IError)
begin
if Assigned(err) then
callback(nil, 0, err)
else
callback(rcpt, amount, err);
end);
finally
iToken.Free;
end;
end);
end;
{ TiToken }
function TiToken.ListenForLatestBlock: Boolean;
begin
Result := inherited ListenForLatestBlock
or Assigned(FOnMint)
or Assigned(FOnBurn);
end;
procedure TiToken.OnLatestBlockMined(log: TLog);
begin
inherited OnLatestBlockMined(log);
if Assigned(FOnMint) then
if log.isEvent('Mint(address,uint256,uint256,uint256)') then
// emitted upon a successful Mint
FOnMint(Self,
log.Topic[1].toAddress, // minter
log.Data[0].toBigInt, // token amount
log.Data[1].toBigInt, // asset amount
log.Data[2].toBigInt); // price
if Assigned(FOnBurn) then
if log.isEvent('Burn(address,uint256,uint256,uint256)') then
// emitted upon a successful Burn
FOnBurn(Self,
log.Topic[1].toAddress, // burner
log.Data[0].toBigInt, // token amount
log.Data[1].toBigInt, // asset amount
log.Data[2].toBigInt); // price
end;
procedure TiToken.SetOnMint(Value: TOnMint);
begin
FOnMint := Value;
EventChanged;
end;
procedure TiToken.SetOnBurn(Value: TOnBurn);
begin
FOnBurn := Value;
EventChanged;
end;
// Called to redeem owned iTokens for an equivalent amount of the underlying asset, at the current tokenPrice() rate.
// The supplier will receive the asset proceeds.
procedure TiToken.Burn(from: TPrivateKey; amount: BigInteger; callback: TAsyncReceipt);
begin
from.Address(procedure(addr: TAddress; err: IError)
begin
if Assigned(err) then
callback(nil, err)
else
web3.eth.write(
Client, from, Contract,
'burn(address,uint256)', [addr, web3.utils.toHex(amount)], callback);
end);
end;
// Called to deposit assets to the iToken, which in turn mints iTokens to the lenders wallet at the current tokenPrice() rate.
// A prior ERC20 approve transaction should have been sent to the asset token for an amount greater than or equal to the specified amount.
// The supplier will receive the minted iTokens.
procedure TiToken.Mint(from: TPrivateKey; amount: BigInteger; callback: TAsyncReceipt);
begin
from.Address(procedure(addr: TAddress; err: IError)
begin
if Assigned(err) then
callback(nil, err)
else
web3.eth.write(
Client, from, Contract,
'mint(address,uint256)', [addr, web3.utils.toHex(amount)], callback);
end);
end;
// Returns the users balance of the underlying asset, scaled by 1e18
// This is the same as multiplying the users token balance by the token price.
procedure TiToken.AssetBalanceOf(owner: TAddress; callback: TAsyncQuantity);
begin
web3.eth.call(Client, Contract, 'assetBalanceOf(address)', [owner], callback);
end;
// Returns the underlying asset contract address for this iToken.
procedure TiToken.LoanTokenAddress(callback: TAsyncAddress);
begin
web3.eth.call(Client, Contract, 'loanTokenAddress()', [], procedure(const hex: string; err: IError)
begin
if Assigned(err) then
callback(ADDRESS_ZERO, err)
else
callback(TAddress.New(hex), nil);
end);
end;
// Returns the aggregate rate that all lenders are receiving from borrowers, scaled by 1e18
procedure TiToken.SupplyInterestRate(callback: TAsyncQuantity);
begin
web3.eth.call(Client, Contract, 'supplyInterestRate()', [], callback);
end;
// Returns the current price of the iToken, scaled by 1e18
procedure TiToken.TokenPrice(callback: TAsyncQuantity);
begin
web3.eth.call(Client, Contract, 'tokenPrice()', [], callback);
end;
{ TiDAI }
constructor TiDAI.Create(aClient: TWeb3);
begin
// https://bzx.network/itokens
if aClient.Chain = Mainnet then
inherited Create(aClient, '0x6b093998d36f2c7f0cc359441fbb24cc629d5ff0')
else
if aClient.Chain = Kovan then
inherited Create(aClient, '0x73d0B4834Ba4ADa053d8282c02305eCdAC2304f0')
else
raise EFulcrum.CreateFmt('iDAI is not deployed on %s', [GetEnumName(TypeInfo(TChain), Integer(aClient.Chain))]);
end;
{ TiUSDC }
constructor TiUSDC.Create(aClient: TWeb3);
begin
// https://bzx.network/itokens
if aClient.Chain = Mainnet then
inherited Create(aClient, '0x32e4c68b3a4a813b710595aeba7f6b7604ab9c15')
else
if aClient.Chain = Kovan then
inherited Create(aClient, '0xaaC9822F31e5Aefb32bC228DcF259F23B49B9855')
else
raise EFulcrum.CreateFmt('iUSDC is not deployed on %s', [GetEnumName(TypeInfo(TChain), Integer(aClient.Chain))]);
end;
{ TiUSDT }
constructor TiUSDT.Create(aClient: TWeb3);
begin
// https://bzx.network/itokens
if aClient.Chain = Mainnet then
inherited Create(aClient, '0x7e9997a38a439b2be7ed9c9c4628391d3e055d48')
else
if aClient.Chain = Kovan then
inherited Create(aClient, '0x6b9F03e05423cC8D00617497890C0872FF33d4E8')
else
if aClient.Chain = BSC_main_net then
inherited Create(aClient, '0xf326b42a237086f1de4e7d68f2d2456fc787bc01')
else
raise EFulcrum.CreateFmt('iUSDT is not deployed on %s', [GetEnumName(TypeInfo(TChain), Integer(aClient.Chain))]);
end;
end.
1
https://gitee.com/a200332/delphereum.git
git@gitee.com:a200332/delphereum.git
a200332
delphereum
delphereum
master

搜索帮助