solution: track balance of a ERC20 token
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package io.emeraldpay.dshackle.rpc
|
||||
|
||||
import io.emeraldpay.api.proto.Common
|
||||
import io.infinitape.etherjar.domain.Address
|
||||
import reactor.test.StepVerifier
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.time.Duration
|
||||
|
||||
class EthereumAddressesSpec extends Specification {
|
||||
|
||||
EthereumAddresses ethereumAddresses = new EthereumAddresses()
|
||||
|
||||
def "Extract single address"() {
|
||||
setup:
|
||||
def address = Common.AnyAddress.newBuilder()
|
||||
.setAddressSingle(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0xab5c66752a9e8167967685f1450532fb96d5d24f")
|
||||
)
|
||||
.build()
|
||||
when:
|
||||
def act = ethereumAddresses.extract(address)
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectNext(Address.from("0xab5c66752a9e8167967685f1450532fb96d5d24f"))
|
||||
.expectComplete().verify(Duration.ofSeconds(1))
|
||||
|
||||
}
|
||||
|
||||
def "Extract single addresses passed as multi"() {
|
||||
setup:
|
||||
def address = Common.AnyAddress.newBuilder()
|
||||
.setAddressMulti(
|
||||
Common.MultiAddress.newBuilder()
|
||||
.addAddresses(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0xab5c66752a9e8167967685f1450532fb96d5d24f")
|
||||
)
|
||||
)
|
||||
.build()
|
||||
when:
|
||||
def act = ethereumAddresses.extract(address)
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectNext(Address.from("0xab5c66752a9e8167967685f1450532fb96d5d24f"))
|
||||
.expectComplete().verify(Duration.ofSeconds(1))
|
||||
|
||||
}
|
||||
|
||||
def "Extract two addresses"() {
|
||||
setup:
|
||||
def address = Common.AnyAddress.newBuilder()
|
||||
.setAddressMulti(
|
||||
Common.MultiAddress.newBuilder()
|
||||
.addAddresses(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0xab5c66752a9e8167967685f1450532fb96d5d24f")
|
||||
)
|
||||
.addAddresses(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0xfdb16996831753d5331ff813c29a93c76834a0ad")
|
||||
)
|
||||
)
|
||||
.build()
|
||||
when:
|
||||
def act = ethereumAddresses.extract(address)
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectNext(Address.from("0xab5c66752a9e8167967685f1450532fb96d5d24f"))
|
||||
.expectNext(Address.from("0xfdb16996831753d5331ff813c29a93c76834a0ad"))
|
||||
.expectComplete().verify(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
def "Extract fes addresses"() {
|
||||
setup:
|
||||
def address = Common.AnyAddress.newBuilder()
|
||||
.setAddressMulti(
|
||||
Common.MultiAddress.newBuilder()
|
||||
.addAddresses(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0xab5c66752a9e8167967685f1450532fb96d5d24f")
|
||||
)
|
||||
.addAddresses(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0xfdb16996831753d5331ff813c29a93c76834a0ad")
|
||||
)
|
||||
.addAddresses(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0x46705dfff24256421a05d056c29e81bdc09723b8")
|
||||
)
|
||||
.addAddresses(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0xadb2b42f6bd96f5c65920b9ac88619dce4166f94")
|
||||
)
|
||||
.addAddresses(
|
||||
Common.SingleAddress.newBuilder()
|
||||
.setAddress("0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852")
|
||||
)
|
||||
)
|
||||
.build()
|
||||
when:
|
||||
def act = ethereumAddresses.extract(address)
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectNext(Address.from("0xab5c66752a9e8167967685f1450532fb96d5d24f"))
|
||||
.expectNext(Address.from("0xfdb16996831753d5331ff813c29a93c76834a0ad"))
|
||||
.expectNext(Address.from("0x46705dfff24256421a05d056c29e81bdc09723b8"))
|
||||
.expectNext(Address.from("0xadb2b42f6bd96f5c65920b9ac88619dce4166f94"))
|
||||
.expectNext(Address.from("0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852"))
|
||||
.expectComplete().verify(Duration.ofSeconds(1))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package io.emeraldpay.dshackle.rpc
|
||||
|
||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||
import io.emeraldpay.api.proto.Common
|
||||
import io.emeraldpay.dshackle.config.TokensConfig
|
||||
import io.emeraldpay.dshackle.test.EthereumUpstreamMock
|
||||
import io.emeraldpay.dshackle.test.MultistreamHolderMock
|
||||
import io.emeraldpay.dshackle.test.ReaderMock
|
||||
import io.emeraldpay.dshackle.upstream.Multistream
|
||||
import io.emeraldpay.dshackle.upstream.MultistreamHolder
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import io.emeraldpay.grpc.Chain
|
||||
import io.infinitape.etherjar.domain.Address
|
||||
import io.infinitape.etherjar.erc20.ERC20Token
|
||||
import io.infinitape.etherjar.hex.HexData
|
||||
import io.infinitape.etherjar.rpc.json.TransactionCallJson
|
||||
import reactor.core.publisher.Mono
|
||||
import spock.lang.Specification
|
||||
|
||||
class TrackERC20AddressSpec extends Specification {
|
||||
|
||||
def "Init with single token"() {
|
||||
setup:
|
||||
MultistreamHolder ups = Mock(MultistreamHolder) {
|
||||
_ * isAvailable(Chain.ETHEREUM) >> true
|
||||
}
|
||||
TokensConfig tokens = new TokensConfig([
|
||||
new TokensConfig.Token().tap {
|
||||
id = "dai"
|
||||
blockchain = Chain.ETHEREUM
|
||||
name = "DAI"
|
||||
type = TokensConfig.Type.ERC20
|
||||
address = Address.from("0x6B175474E89094C44Da98b954EedeAC495271d0F")
|
||||
}
|
||||
])
|
||||
TrackERC20Address track = new TrackERC20Address(ups, tokens)
|
||||
when:
|
||||
track.init()
|
||||
def act = track.tokens
|
||||
def supportDai = track.isSupported(Chain.ETHEREUM, "DAI")
|
||||
def supportSai = track.isSupported(Chain.ETHEREUM, "SAI")
|
||||
|
||||
then:
|
||||
act.size() == 1
|
||||
with(act.keySet().first()) {
|
||||
chain == Chain.ETHEREUM
|
||||
name == "dai"
|
||||
}
|
||||
with(act.values().first()) {
|
||||
chain == Chain.ETHEREUM
|
||||
name == "dai"
|
||||
token != null
|
||||
}
|
||||
supportDai
|
||||
!supportSai
|
||||
}
|
||||
|
||||
def "Init without tokens"() {
|
||||
setup:
|
||||
MultistreamHolder ups = Mock(MultistreamHolder) {
|
||||
_ * isAvailable(Chain.ETHEREUM) >> true
|
||||
}
|
||||
|
||||
TokensConfig tokens = new TokensConfig([])
|
||||
TrackERC20Address track = new TrackERC20Address(ups, tokens)
|
||||
when:
|
||||
track.init()
|
||||
def act = track.tokens
|
||||
def supportDai = track.isSupported(Chain.ETHEREUM, "dai")
|
||||
def supportSai = track.isSupported(Chain.ETHEREUM, "sai")
|
||||
|
||||
then:
|
||||
act.size() == 0
|
||||
!supportDai
|
||||
!supportSai
|
||||
}
|
||||
|
||||
def "Init with two tokens"() {
|
||||
setup:
|
||||
MultistreamHolder ups = Mock(MultistreamHolder) {
|
||||
_ * isAvailable(Chain.ETHEREUM) >> true
|
||||
}
|
||||
|
||||
TokensConfig tokens = new TokensConfig([
|
||||
new TokensConfig.Token().tap {
|
||||
id = "dai"
|
||||
blockchain = Chain.ETHEREUM
|
||||
name = "DAI"
|
||||
type = TokensConfig.Type.ERC20
|
||||
address = Address.from("0x6B175474E89094C44Da98b954EedeAC495271d0F")
|
||||
},
|
||||
new TokensConfig.Token().tap {
|
||||
id = "sai"
|
||||
blockchain = Chain.ETHEREUM
|
||||
name = "SAI"
|
||||
type = TokensConfig.Type.ERC20
|
||||
address = Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9")
|
||||
}
|
||||
])
|
||||
TrackERC20Address track = new TrackERC20Address(ups, tokens)
|
||||
when:
|
||||
track.init()
|
||||
def act = track.tokens
|
||||
def supportDai = track.isSupported(Chain.ETHEREUM, "dai")
|
||||
def supportSai = track.isSupported(Chain.ETHEREUM, "sai")
|
||||
|
||||
then:
|
||||
act.size() == 2
|
||||
with(act[act.keySet().find { it.name == "dai" }]) {
|
||||
chain == Chain.ETHEREUM
|
||||
name == "dai"
|
||||
}
|
||||
with(act[act.keySet().find { it.name == "sai" }]) {
|
||||
chain == Chain.ETHEREUM
|
||||
name == "sai"
|
||||
}
|
||||
supportDai
|
||||
supportSai
|
||||
}
|
||||
|
||||
def "Gets balance from upstream"() {
|
||||
setup:
|
||||
ReaderMock api = new ReaderMock()
|
||||
.with(
|
||||
new JsonRpcRequest("eth_call", [
|
||||
new TransactionCallJson().tap { json ->
|
||||
json.setTo(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9"))
|
||||
json.setData(HexData.from("0x70a0823100000000000000000000000016c15c65ad00b6dfbcc2cb8a7b6c2d0103a3883b"))
|
||||
},
|
||||
"latest"
|
||||
]),
|
||||
JsonRpcResponse.ok('"0x0000000000000000000000000000000000000000000000000000001f28d72868"')
|
||||
)
|
||||
|
||||
EthereumUpstream upstream = new EthereumUpstreamMock(Chain.ETHEREUM, api)
|
||||
MultistreamHolder ups = new MultistreamHolderMock(Chain.ETHEREUM, upstream)
|
||||
TrackERC20Address track = new TrackERC20Address(ups, new TokensConfig([]))
|
||||
|
||||
TrackERC20Address.TrackedAddress address = new TrackERC20Address.TrackedAddress(
|
||||
Chain.ETHEREUM,
|
||||
Address.from("0x16c15c65ad00b6dfbcc2cb8a7b6c2d0103a3883b"),
|
||||
new ERC20Token(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9")),
|
||||
"test",
|
||||
BigInteger.valueOf(1234)
|
||||
)
|
||||
when:
|
||||
def act = track.getBalance(address).block()
|
||||
|
||||
then:
|
||||
act.toLong() == 0x1f28d72868
|
||||
}
|
||||
|
||||
def "Builds response"() {
|
||||
setup:
|
||||
TrackERC20Address track = new TrackERC20Address(Stub(MultistreamHolder), new TokensConfig([]))
|
||||
TrackERC20Address.TrackedAddress address = new TrackERC20Address.TrackedAddress(
|
||||
Chain.ETHEREUM,
|
||||
Address.from("0x16c15c65ad00b6dfbcc2cb8a7b6c2d0103a3883b"),
|
||||
new ERC20Token(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9")),
|
||||
"test",
|
||||
BigInteger.valueOf(1234)
|
||||
)
|
||||
when:
|
||||
def act = track.buildResponse(address)
|
||||
then:
|
||||
act == BlockchainOuterClass.AddressBalance.newBuilder()
|
||||
.setAddress(Common.SingleAddress.newBuilder().setAddress("0x16c15c65ad00b6dfbcc2cb8a7b6c2d0103a3883b"))
|
||||
.setAsset(Common.Asset.newBuilder()
|
||||
.setChain(Common.ChainRef.CHAIN_ETHEREUM)
|
||||
.setCode("TEST")
|
||||
)
|
||||
.setBalance("1234")
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,10 @@ class ReaderMock<K, D> implements Reader<K, D> {
|
||||
|
||||
@Override
|
||||
Mono<D> read(K key) {
|
||||
return Mono.justOrEmpty(mapping.get(key))
|
||||
def value = mapping.get(key)
|
||||
if (value == null) {
|
||||
println("No mocked value for request: $key")
|
||||
}
|
||||
return Mono.justOrEmpty(value)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user