Compare commits

..
16 Commits
Author SHA1 Message Date
Github Actions 28355424bd [CI Skip] release/stable 1.33.1
skip-checks: true
2020-09-14 07:11:20 +00:00
Jaco Greeff e7be062522 1.33 (#2602) 2020-09-14 08:58:21 +02:00
Github Actions ed5b5eeaa6 [CI Skip] release/beta 1.33.0-beta.6
skip-checks: true
2020-09-13 12:34:01 +00:00
Jaco Greeff ad8ad42344 Map Websocket error codes to short descriptions (#2601) 2020-09-13 14:23:00 +02:00
Github Actions f311e5d57f [CI Skip] release/beta 1.33.0-beta.5
skip-checks: true
2020-09-10 06:43:32 +00:00
Jaco Greeff 065f39ba6d Explicit hash to paymentInfo for query (#2598)
* Explicit hash to paymentInfo for query

* CHANGELOG
2020-09-10 08:30:16 +02:00
Github Actions 64bf72a6d7 [CI Skip] release/beta 1.33.0-beta.4
skip-checks: true
2020-09-10 00:50:50 +00:00
Jaco Greeff b59b60ae1f Use source registry in clones (#2597) 2020-09-10 02:38:07 +02:00
Github Actions e5b7e7f16f [CI Skip] release/beta 1.33.0-beta.3
skip-checks: true
2020-09-09 16:56:29 +00:00
Jaco Greeff c2705bdfda Set runtimeChain from source (#2595)
* Set runtimeChain from source

* Update packages/api/src/base/index.ts

* Update CHANGELOG.md
2020-09-09 18:44:47 +02:00
Github Actions 373eb06713 [CI Skip] release/beta 1.33.0-beta.2
skip-checks: true
2020-09-09 11:43:42 +00:00
Jaco Greeff 45c2c40a3e derive.staking.stakerPrefs (#2594) 2020-09-09 13:31:06 +02:00
Github Actions 3b110d7d7f [CI Skip] release/beta 1.33.0-beta.1
skip-checks: true
2020-09-09 08:55:49 +00:00
Jaco Greeff 0a8d2e5ac1 Allow as-signed paymentInfo on any extrinsic (#2593)
* Allow as-signed paymentInfo on any extrinsic

* Bumps
2020-09-09 10:43:59 +02:00
Github Actions d0bf8b8d82 [CI Skip] release/beta 1.33.0-beta.0
skip-checks: true
2020-09-07 11:39:53 +00:00
Jaco Greeff 2c3ef10513 Optimize heartbeat retrievals (#2590) 2020-09-07 13:28:43 +02:00
25 changed files with 480 additions and 638 deletions
+14
View File
@@ -1,5 +1,19 @@
# CHANGELOG
## 1.33.1 Sep 14, 2020
Upgrade priority: Low. Recommended when using clones instances.
Changes:
- Allow `paymentInfo` on any extrinsic with `tx.paymentInfo(<address>, <at>)` (hash specified)
- When cloning an API instance the runtimeChain is now properly set from source
- When cloning an API instance the registry is shared with the source
- Optimize derive `receivedHeartbeats` to not re-create the full object
- Add `staking.stakerPrefs` derive to retrieve validatorPrefs over a range of eras
- Basic map of Websocket error codes to short descriptions (where none available)
## 1.32.1 Sep 7, 2020
Upgrade priority: Low. Recommended when manually using provider connect/disconnect or using multiple instances in a single process.
+1 -1
View File
@@ -9,5 +9,5 @@
"packages": [
"packages/*"
],
"version": "1.32.1"
"version": "1.33.1"
}
+4 -4
View File
@@ -28,15 +28,15 @@
"@babel/core": "^7.11.6",
"@babel/register": "^7.11.5",
"@babel/runtime": "^7.11.2",
"@polkadot/dev": "^0.55.47",
"@polkadot/ts": "^0.3.43",
"@polkadot/dev": "^0.55.51",
"@polkadot/ts": "^0.3.46",
"@polkadot/typegen": "workspace:packages/typegen",
"@types/jest": "^26.0.13",
"@vuepress/plugin-search": "^1.5.4",
"copyfiles": "^2.3.0",
"typedoc": "^0.19.1",
"typedoc-plugin-markdown": "^2.4.2",
"typedoc-plugin-no-inherit": "^1.1.10"
"typedoc-plugin-no-inherit": "^1.2.0"
},
"version": "1.32.1"
"version": "1.33.1"
}
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/api-contract",
"version": "1.32.1",
"version": "1.33.1",
"description": "Interfaces for interacting with contracts and contract ABIs",
"main": "index.js",
"keywords": [
@@ -27,11 +27,11 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api-contract#readme",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@polkadot/api": "1.32.1",
"@polkadot/rpc-core": "1.32.1",
"@polkadot/types": "1.32.1",
"@polkadot/api": "1.33.1",
"@polkadot/rpc-core": "1.33.1",
"@polkadot/types": "1.33.1",
"@polkadot/util": "^3.4.1",
"bn.js": "^5.1.3",
"rxjs": "^6.6.2"
"rxjs": "^6.6.3"
}
}
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/api-derive",
"version": "1.32.1",
"version": "1.33.1",
"description": "Common functions used across Polkadot, derived from RPC calls and storage queries.",
"main": "index.js",
"keywords": [
@@ -28,15 +28,15 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api-derive#readme",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@polkadot/api": "1.32.1",
"@polkadot/rpc-core": "1.32.1",
"@polkadot/rpc-provider": "1.32.1",
"@polkadot/types": "1.32.1",
"@polkadot/api": "1.33.1",
"@polkadot/rpc-core": "1.33.1",
"@polkadot/rpc-provider": "1.33.1",
"@polkadot/types": "1.33.1",
"@polkadot/util": "^3.4.1",
"@polkadot/util-crypto": "^3.4.1",
"bn.js": "^5.1.3",
"memoizee": "^0.4.14",
"rxjs": "^6.6.2"
"rxjs": "^6.6.3"
},
"devDependencies": {
"@polkadot/keyring": "^3.4.1"
@@ -13,6 +13,25 @@ import { BN_ZERO } from '@polkadot/util';
import { memo } from '../util';
function mapResult ([result, validators, heartbeats, numBlocks]: [DeriveHeartbeats, AccountId[], Option<Bytes>[], u32[]]): DeriveHeartbeats {
validators.forEach((validator, index): void => {
const validatorId = validator.toString();
const blockCount = numBlocks[index];
const hasMessage = !heartbeats[index].isEmpty;
const prev = result[validatorId];
if (!prev || prev.hasMessage !== hasMessage || !prev.blockCount.eq(blockCount)) {
result[validatorId] = {
blockCount,
hasMessage,
isOnline: hasMessage || blockCount.gt(BN_ZERO)
};
}
});
return result;
}
/**
* @description Return a boolean array indicating whether the passed accounts had received heartbeats in the current session
*/
@@ -20,8 +39,9 @@ export function receivedHeartbeats (instanceId: string, api: ApiInterfaceRx): ()
return memo(instanceId, (): Observable<DeriveHeartbeats> =>
api.query.imOnline?.receivedHeartbeats
? api.derive.staking.overview().pipe(
switchMap(({ currentIndex, validators }): Observable<[AccountId[], Option<Bytes>[], u32[]]> =>
switchMap(({ currentIndex, validators }): Observable<[DeriveHeartbeats, AccountId[], Option<Bytes>[], u32[]]> =>
combineLatest([
of({}),
of(validators),
api.query.imOnline.receivedHeartbeats.multi<Option<Bytes>>(
validators.map((_address, index) => [currentIndex, index])),
@@ -29,16 +49,8 @@ export function receivedHeartbeats (instanceId: string, api: ApiInterfaceRx): ()
validators.map((address) => [currentIndex, address]))
])
),
map(([validators, heartbeats, numBlocks]): DeriveHeartbeats =>
validators.reduce((result: DeriveHeartbeats, validator, index): DeriveHeartbeats => ({
...result,
[validator.toString()]: {
blockCount: numBlocks[index],
hasMessage: !heartbeats[index].isEmpty,
isOnline: !heartbeats[index].isEmpty || numBlocks[index].gt(BN_ZERO)
}
}), {})
)
map(mapResult)
)
: of({}));
: of({})
);
}
+1
View File
@@ -18,6 +18,7 @@ export * from './ownSlashes';
export * from './query';
export * from './stakerExposure';
export * from './stakerPoints';
export * from './stakerPrefs';
export * from './stakerRewards';
export * from './stakerSlashes';
export * from './stashes';
@@ -0,0 +1,34 @@
// Copyright 2017-2020 @polkadot/api-derive authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ApiInterfaceRx } from '@polkadot/api/types';
import { EraIndex, ValidatorPrefs } from '@polkadot/types/interfaces';
import { DeriveStakerPrefs } from '../types';
import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { memo } from '../util';
export function _stakerPrefs (instanceId: string, api: ApiInterfaceRx): (accountId: Uint8Array | string, eras: EraIndex[], withActive: boolean) => Observable<DeriveStakerPrefs[]> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return memo(instanceId, (accountId: Uint8Array | string, eras: EraIndex[], _withActive: boolean): Observable<DeriveStakerPrefs[]> => {
return api.query.staking.erasValidatorPrefs.multi<ValidatorPrefs>(eras.map((era) => [era, accountId])).pipe(
map((all): DeriveStakerPrefs[] =>
all.map((validatorPrefs, index): DeriveStakerPrefs => ({
era: eras[index],
validatorPrefs
}))
)
);
});
}
export function stakerPrefs (instanceId: string, api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable<DeriveStakerPrefs[]> {
return memo(instanceId, (accountId: Uint8Array | string, withActive = false): Observable<DeriveStakerPrefs[]> =>
api.derive.staking.erasHistoric(withActive).pipe(
switchMap((eras) => api.derive.staking._stakerPrefs(accountId, eras, withActive))
)
);
}
+5
View File
@@ -69,6 +69,11 @@ export interface DeriveStakerExposure {
validators: DeriveEraValidatorExposure;
}
export interface DeriveStakerPrefs {
era: EraIndex;
validatorPrefs: ValidatorPrefs;
}
export interface DeriveStakerRewardValidator {
total: Balance;
value: Balance;
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/api",
"version": "1.32.1",
"version": "1.33.1",
"description": "Promise and RxJS wrappers around the Polkadot JS RPC",
"main": "index.js",
"keywords": [
@@ -27,18 +27,18 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api#readme",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@polkadot/api-derive": "1.32.1",
"@polkadot/api-derive": "1.33.1",
"@polkadot/keyring": "^3.4.1",
"@polkadot/metadata": "1.32.1",
"@polkadot/rpc-core": "1.32.1",
"@polkadot/rpc-provider": "1.32.1",
"@polkadot/types": "1.32.1",
"@polkadot/types-known": "1.32.1",
"@polkadot/metadata": "1.33.1",
"@polkadot/rpc-core": "1.33.1",
"@polkadot/rpc-provider": "1.33.1",
"@polkadot/types": "1.33.1",
"@polkadot/types-known": "1.33.1",
"@polkadot/util": "^3.4.1",
"@polkadot/util-crypto": "^3.4.1",
"bn.js": "^5.1.3",
"eventemitter3": "^4.0.7",
"rxjs": "^6.6.2"
"rxjs": "^6.6.3"
},
"devDependencies": {
"@polkadot/keyring": "^3.4.1"
+1 -1
View File
@@ -132,7 +132,7 @@ export default abstract class Decorate<ApiType extends ApiTypes> extends Events
super();
this.#instanceId = `${++instanceCounter}`;
this.#registry = options.registry || new TypeRegistry();
this.#registry = options.source?.registry || options.registry || new TypeRegistry();
const thisProvider = options.source
? options.source._rpcCore.provider.clone()
+2 -2
View File
@@ -45,7 +45,7 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
if (!options.source) {
this.registerTypes(options.types);
} else {
this.registry.setKnownTypes(options.source.registry.knownTypes);
this.#registries = options.source.#registries;
}
this._rpc = this._decorateRpc(this._rpcCore, this._decorateMethod);
@@ -161,9 +161,9 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
// eslint-disable-next-line @typescript-eslint/require-await
private async _metaFromSource (source: ApiBase<any>): Promise<Metadata> {
this._extrinsicType = source.extrinsicVersion;
this._runtimeChain = source.runtimeChain;
this._runtimeVersion = source.runtimeVersion;
this._genesisHash = source.genesisHash;
this.registry.setChainProperties(source.registry.getChainProperties());
const methods: string[] = [];
+8 -1
View File
@@ -7,7 +7,7 @@ import { Hash, RuntimeVersion } from '@polkadot/types/interfaces';
import { CallFunction, RegistryError, SignerPayloadRawBase } from '@polkadot/types/types';
import { ApiInterfaceRx, ApiOptions, ApiTypes, DecoratedRpc, DecorateMethod, QueryableConsts, QueryableStorage, QueryableStorageMulti, SubmittableExtrinsics, Signer } from '../types';
import { Metadata } from '@polkadot/types';
import { Metadata, Text } from '@polkadot/types';
import { assert, isString, isUndefined, u8aToHex, u8aToU8a } from '@polkadot/util';
import Init from './Init';
@@ -193,6 +193,13 @@ export default abstract class ApiBase<ApiType extends ApiTypes> extends Init<Api
return assertResult(this._rpc);
}
/**
* @description Contains the chain information for the current node.
*/
public get runtimeChain (): Text {
return assertResult(this._runtimeChain);
}
/**
* @description Yields the current attached runtime metadata. Generally this is only used to construct extrinsics & storage, but is useful for current runtime inspection.
*/
+10 -3
View File
@@ -11,7 +11,7 @@ import { AddressOrPair, SignerOptions, SubmittableExtrinsic, SubmittablePaymentR
import { Observable, of } from 'rxjs';
import { first, map, mapTo, mergeMap, switchMap, tap } from 'rxjs/operators';
import { assert, isBn, isFunction, isNumber } from '@polkadot/util';
import { assert, isBn, isFunction, isNumber, isString, isU8a } from '@polkadot/util';
import { filterEvents, isKeyringPair } from '../util';
import ApiBase from '../base';
@@ -37,8 +37,15 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
}
// calculate the payment info for this transaction (if signed and submitted)
public paymentInfo (account: AddressOrPair, options?: Partial<SignerOptions>): SubmittablePaymentResult<ApiType> {
const [allOptions] = this.#makeSignAndSendOptions(options);
public paymentInfo (account: AddressOrPair, optionsOrHash?: Partial<SignerOptions> | Uint8Array | string): SubmittablePaymentResult<ApiType> {
if (isString(optionsOrHash) || isU8a(optionsOrHash)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return decorateMethod(
() => api.rpc.payment.queryInfo(this.toHex(), optionsOrHash)
);
}
const [allOptions] = this.#makeSignAndSendOptions(optionsOrHash);
const address = isKeyringPair(account) ? account.address : account.toString();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call
+2 -2
View File
@@ -3,7 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountId, Address, Extrinsic, ExtrinsicStatus, EventRecord, Hash, RuntimeDispatchInfo } from '@polkadot/types/interfaces';
import { AnyNumber, AnyU8a, Callback, Codec, IExtrinsicEra, IKeyringPair, Signer, ISubmittableResult } from '@polkadot/types/types';
import { AnyNumber, Callback, Codec, IExtrinsicEra, IKeyringPair, Signer, ISubmittableResult } from '@polkadot/types/types';
import { ApiTypes } from '../types';
import { Observable } from 'rxjs';
@@ -34,7 +34,7 @@ export type SubmittableThis<ApiType extends ApiTypes, THIS> =
: Promise<THIS>;
export interface SignerOptions {
blockHash: AnyU8a;
blockHash: Uint8Array | string;
era?: IExtrinsicEra | number;
nonce: AnyNumber | Codec;
signer?: Signer;
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/metadata",
"version": "1.32.1",
"version": "1.33.1",
"description": "Helpers to extract information from runtime metadata",
"main": "index.js",
"publishConfig": {
@@ -27,8 +27,8 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/type-metadata#readme",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@polkadot/types": "1.32.1",
"@polkadot/types-known": "1.32.1",
"@polkadot/types": "1.33.1",
"@polkadot/types-known": "1.33.1",
"@polkadot/util": "^3.4.1",
"@polkadot/util-crypto": "^3.4.1",
"bn.js": "^5.1.3"
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/rpc-core",
"version": "1.32.1",
"version": "1.33.1",
"description": "A JavaScript wrapper for the Polkadot JsonRPC interface",
"main": "index.js",
"keywords": [
@@ -27,12 +27,12 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-core#readme",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@polkadot/metadata": "1.32.1",
"@polkadot/rpc-provider": "1.32.1",
"@polkadot/types": "1.32.1",
"@polkadot/metadata": "1.33.1",
"@polkadot/rpc-provider": "1.33.1",
"@polkadot/types": "1.33.1",
"@polkadot/util": "^3.4.1",
"memoizee": "^0.4.14",
"rxjs": "^6.6.2"
"rxjs": "^6.6.3"
},
"devDependencies": {
"@polkadot/keyring": "^3.4.1"
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/rpc-provider",
"version": "1.32.1",
"version": "1.33.1",
"description": "Transport providers for the API",
"main": "index.js",
"keywords": [
@@ -27,8 +27,8 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-provider#readme",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@polkadot/metadata": "1.32.1",
"@polkadot/types": "1.32.1",
"@polkadot/metadata": "1.33.1",
"@polkadot/types": "1.33.1",
"@polkadot/util": "^3.4.1",
"@polkadot/util-crypto": "^3.4.1",
"bn.js": "^5.1.3",
+2 -1
View File
@@ -11,6 +11,7 @@ import { assert, isNull, isUndefined, isChildClass, logger } from '@polkadot/uti
import Coder from '../coder';
import defaults from '../defaults';
import { getWSErrorString } from './errors';
import getWSClass from './getWSClass';
interface SubscriptionHandler {
@@ -314,7 +315,7 @@ export default class WsProvider implements ProviderInterface {
#onSocketClose = (event: CloseEvent): void => {
if (this.#autoConnectMs > 0) {
l.error(`disconnected from ${this.#endpoints[this.#endpointIndex]} code: '${event.code}' reason: '${event.reason}'`);
l.error(`disconnected from ${this.#endpoints[this.#endpointIndex]}: ${event.code}:: ${event.reason || getWSErrorString(event.code)}`);
}
this.#isConnected = false;
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2017-2020 @polkadot/rpc-provider authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
// from https://stackoverflow.com/questions/19304157/getting-the-reason-why-websockets-closed-with-close-code-1006
const specificStatusCodeMappings: Record<number, string> = {
1000: 'Normal Closure',
1001: 'Going Away',
1002: 'Protocol Error',
1003: 'Unsupported Data',
1004: '(For future)',
1005: 'No Status Received',
1006: 'Abnormal Closure',
1007: 'Invalid frame payload data',
1008: 'Policy Violation',
1009: 'Message too big',
1010: 'Missing Extension',
1011: 'Internal Error',
1012: 'Service Restart',
1013: 'Try Again Later',
1014: 'Bad Gateway',
1015: 'TLS Handshake'
};
export function getWSErrorString (code: number): string {
if (code >= 0 && code <= 999) {
return '(Unused)';
} else if (code >= 1016) {
if (code <= 1999) {
return '(For WebSocket standard)';
} else if (code <= 2999) {
return '(For WebSocket extensions)';
} else if (code <= 3999) {
return '(For libraries and frameworks)';
} else if (code <= 4999) {
return '(For applications)';
}
}
return specificStatusCodeMappings[code] || '(Unknown)';
}
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/typegen",
"version": "1.32.1",
"version": "1.33.1",
"description": "Type generation scripts",
"main": "index.js",
"bin": {
@@ -36,14 +36,14 @@
"@babel/core": "^7.11.6",
"@babel/register": "^7.11.5",
"@babel/runtime": "^7.11.2",
"@polkadot/api": "1.32.1",
"@polkadot/metadata": "1.32.1",
"@polkadot/rpc-provider": "1.32.1",
"@polkadot/types": "1.32.1",
"@polkadot/api": "1.33.1",
"@polkadot/metadata": "1.33.1",
"@polkadot/rpc-provider": "1.33.1",
"@polkadot/types": "1.33.1",
"@polkadot/util": "^3.4.1",
"handlebars": "^4.7.6",
"websocket": "^1.0.32",
"yargs": "^15.4.1"
"yargs": "^16.0.3"
},
"devDependencies": {
"@types/websocket": "^1.0.1",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/types-known",
"version": "1.32.1",
"version": "1.33.1",
"description": "A JavaScript wrapper for the Polkadot JsonRPC interface",
"main": "index.js",
"keywords": [
@@ -27,7 +27,7 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/types-known#readme",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@polkadot/types": "1.32.1",
"@polkadot/types": "1.33.1",
"@polkadot/util": "^3.4.1",
"bn.js": "^5.1.3"
},
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/types",
"version": "1.32.1",
"version": "1.33.1",
"description": "Implementation of the Parity codec",
"main": "index.js",
"keywords": [
@@ -27,13 +27,13 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/types#readme",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@polkadot/metadata": "1.32.1",
"@polkadot/metadata": "1.33.1",
"@polkadot/util": "^3.4.1",
"@polkadot/util-crypto": "^3.4.1",
"@types/bn.js": "^4.11.6",
"bn.js": "^5.1.3",
"memoizee": "^0.4.14",
"rxjs": "^6.6.2"
"rxjs": "^6.6.3"
},
"devDependencies": {
"@polkadot/keyring": "^3.4.1",
+2 -2
View File
@@ -150,9 +150,9 @@ export interface IExtrinsicEra extends Codec {
}
export interface SignatureOptions {
blockHash: AnyU8a;
blockHash: Uint8Array | string;
era?: IExtrinsicEra;
genesisHash: AnyU8a;
genesisHash: Uint8Array | string;
nonce: AnyNumber;
runtimeVersion: IRuntimeVersion;
signedExtensions?: string[];
+287 -568
View File
File diff suppressed because it is too large Load Diff